diff --git a/.gitignore b/.gitignore index ec84e88..3a14387 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,8 @@ *.pyc .DS_Store credentials.json -venv/ \ No newline at end of file +venv/ +.vscode/ +__pycache__/ +*.pyo +*.pyc \ No newline at end of file diff --git a/lib/protobuf/__init__.py b/lib/protobuf/__init__.py new file mode 100644 index 0000000..03f3b29 --- /dev/null +++ b/lib/protobuf/__init__.py @@ -0,0 +1,33 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# Copyright 2007 Google Inc. All Rights Reserved. + +__version__ = '3.20.1' diff --git a/lib/protobuf/any_pb2.py b/lib/protobuf/any_pb2.py new file mode 100644 index 0000000..9121193 --- /dev/null +++ b/lib/protobuf/any_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/any.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\x19google/protobuf/any.proto\x12\x0fgoogle.protobuf\"&\n\x03\x41ny\x12\x10\n\x08type_url\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\x42v\n\x13\x63om.google.protobufB\x08\x41nyProtoP\x01Z,google.golang.org/protobuf/types/known/anypb\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.any_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\023com.google.protobufB\010AnyProtoP\001Z,google.golang.org/protobuf/types/known/anypb\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _ANY._serialized_start=46 + _ANY._serialized_end=84 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/api_pb2.py b/lib/protobuf/api_pb2.py new file mode 100644 index 0000000..1721b10 --- /dev/null +++ b/lib/protobuf/api_pb2.py @@ -0,0 +1,32 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/api.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 google.protobuf import source_context_pb2 as google_dot_protobuf_dot_source__context__pb2 +from google.protobuf import type_pb2 as google_dot_protobuf_dot_type__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x19google/protobuf/api.proto\x12\x0fgoogle.protobuf\x1a$google/protobuf/source_context.proto\x1a\x1agoogle/protobuf/type.proto\"\x81\x02\n\x03\x41pi\x12\x0c\n\x04name\x18\x01 \x01(\t\x12(\n\x07methods\x18\x02 \x03(\x0b\x32\x17.google.protobuf.Method\x12(\n\x07options\x18\x03 \x03(\x0b\x32\x17.google.protobuf.Option\x12\x0f\n\x07version\x18\x04 \x01(\t\x12\x36\n\x0esource_context\x18\x05 \x01(\x0b\x32\x1e.google.protobuf.SourceContext\x12&\n\x06mixins\x18\x06 \x03(\x0b\x32\x16.google.protobuf.Mixin\x12\'\n\x06syntax\x18\x07 \x01(\x0e\x32\x17.google.protobuf.Syntax\"\xd5\x01\n\x06Method\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x18\n\x10request_type_url\x18\x02 \x01(\t\x12\x19\n\x11request_streaming\x18\x03 \x01(\x08\x12\x19\n\x11response_type_url\x18\x04 \x01(\t\x12\x1a\n\x12response_streaming\x18\x05 \x01(\x08\x12(\n\x07options\x18\x06 \x03(\x0b\x32\x17.google.protobuf.Option\x12\'\n\x06syntax\x18\x07 \x01(\x0e\x32\x17.google.protobuf.Syntax\"#\n\x05Mixin\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0c\n\x04root\x18\x02 \x01(\tBv\n\x13\x63om.google.protobufB\x08\x41piProtoP\x01Z,google.golang.org/protobuf/types/known/apipb\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.api_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\023com.google.protobufB\010ApiProtoP\001Z,google.golang.org/protobuf/types/known/apipb\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _API._serialized_start=113 + _API._serialized_end=370 + _METHOD._serialized_start=373 + _METHOD._serialized_end=586 + _MIXIN._serialized_start=588 + _MIXIN._serialized_end=623 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/compiler/__init__.py b/lib/protobuf/compiler/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/protobuf/compiler/plugin_pb2.py b/lib/protobuf/compiler/plugin_pb2.py new file mode 100644 index 0000000..715a891 --- /dev/null +++ b/lib/protobuf/compiler/plugin_pb2.py @@ -0,0 +1,35 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/compiler/plugin.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 google.protobuf import descriptor_pb2 as google_dot_protobuf_dot_descriptor__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n%google/protobuf/compiler/plugin.proto\x12\x18google.protobuf.compiler\x1a google/protobuf/descriptor.proto\"F\n\x07Version\x12\r\n\x05major\x18\x01 \x01(\x05\x12\r\n\x05minor\x18\x02 \x01(\x05\x12\r\n\x05patch\x18\x03 \x01(\x05\x12\x0e\n\x06suffix\x18\x04 \x01(\t\"\xba\x01\n\x14\x43odeGeneratorRequest\x12\x18\n\x10\x66ile_to_generate\x18\x01 \x03(\t\x12\x11\n\tparameter\x18\x02 \x01(\t\x12\x38\n\nproto_file\x18\x0f \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\x12;\n\x10\x63ompiler_version\x18\x03 \x01(\x0b\x32!.google.protobuf.compiler.Version\"\xc1\x02\n\x15\x43odeGeneratorResponse\x12\r\n\x05\x65rror\x18\x01 \x01(\t\x12\x1a\n\x12supported_features\x18\x02 \x01(\x04\x12\x42\n\x04\x66ile\x18\x0f \x03(\x0b\x32\x34.google.protobuf.compiler.CodeGeneratorResponse.File\x1a\x7f\n\x04\x46ile\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x17\n\x0finsertion_point\x18\x02 \x01(\t\x12\x0f\n\x07\x63ontent\x18\x0f \x01(\t\x12?\n\x13generated_code_info\x18\x10 \x01(\x0b\x32\".google.protobuf.GeneratedCodeInfo\"8\n\x07\x46\x65\x61ture\x12\x10\n\x0c\x46\x45\x41TURE_NONE\x10\x00\x12\x1b\n\x17\x46\x45\x41TURE_PROTO3_OPTIONAL\x10\x01\x42W\n\x1c\x63om.google.protobuf.compilerB\x0cPluginProtosZ)google.golang.org/protobuf/types/pluginpb') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.compiler.plugin_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\034com.google.protobuf.compilerB\014PluginProtosZ)google.golang.org/protobuf/types/pluginpb' + _VERSION._serialized_start=101 + _VERSION._serialized_end=171 + _CODEGENERATORREQUEST._serialized_start=174 + _CODEGENERATORREQUEST._serialized_end=360 + _CODEGENERATORRESPONSE._serialized_start=363 + _CODEGENERATORRESPONSE._serialized_end=684 + _CODEGENERATORRESPONSE_FILE._serialized_start=499 + _CODEGENERATORRESPONSE_FILE._serialized_end=626 + _CODEGENERATORRESPONSE_FEATURE._serialized_start=628 + _CODEGENERATORRESPONSE_FEATURE._serialized_end=684 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/descriptor.py b/lib/protobuf/descriptor.py new file mode 100644 index 0000000..ad70be9 --- /dev/null +++ b/lib/protobuf/descriptor.py @@ -0,0 +1,1224 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Descriptors essentially contain exactly the information found in a .proto +file, in types that make this information accessible in Python. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import threading +import warnings + +from google.protobuf.internal import api_implementation + +_USE_C_DESCRIPTORS = False +if api_implementation.Type() == 'cpp': + # Used by MakeDescriptor in cpp mode + import binascii + import os + from google.protobuf.pyext import _message + _USE_C_DESCRIPTORS = True + + +class Error(Exception): + """Base error for this module.""" + + +class TypeTransformationError(Error): + """Error transforming between python proto type and corresponding C++ type.""" + + +if _USE_C_DESCRIPTORS: + # This metaclass allows to override the behavior of code like + # isinstance(my_descriptor, FieldDescriptor) + # and make it return True when the descriptor is an instance of the extension + # type written in C++. + class DescriptorMetaclass(type): + def __instancecheck__(cls, obj): + if super(DescriptorMetaclass, cls).__instancecheck__(obj): + return True + if isinstance(obj, cls._C_DESCRIPTOR_CLASS): + return True + return False +else: + # The standard metaclass; nothing changes. + DescriptorMetaclass = type + + +class _Lock(object): + """Wrapper class of threading.Lock(), which is allowed by 'with'.""" + + def __new__(cls): + self = object.__new__(cls) + self._lock = threading.Lock() # pylint: disable=protected-access + return self + + def __enter__(self): + self._lock.acquire() + + def __exit__(self, exc_type, exc_value, exc_tb): + self._lock.release() + + +_lock = threading.Lock() + + +def _Deprecated(name): + if _Deprecated.count > 0: + _Deprecated.count -= 1 + warnings.warn( + 'Call to deprecated create function %s(). Note: Create unlinked ' + 'descriptors is going to go away. Please use get/find descriptors from ' + 'generated code or query the descriptor_pool.' + % name, + category=DeprecationWarning, stacklevel=3) + + +# Deprecated warnings will print 100 times at most which should be enough for +# users to notice and do not cause timeout. +_Deprecated.count = 100 + + +_internal_create_key = object() + + +class DescriptorBase(metaclass=DescriptorMetaclass): + + """Descriptors base class. + + This class is the base of all descriptor classes. It provides common options + related functionality. + + Attributes: + has_options: True if the descriptor has non-default options. Usually it + is not necessary to read this -- just call GetOptions() which will + happily return the default instance. However, it's sometimes useful + for efficiency, and also useful inside the protobuf implementation to + avoid some bootstrapping issues. + """ + + if _USE_C_DESCRIPTORS: + # The class, or tuple of classes, that are considered as "virtual + # subclasses" of this descriptor class. + _C_DESCRIPTOR_CLASS = () + + def __init__(self, options, serialized_options, options_class_name): + """Initialize the descriptor given its options message and the name of the + class of the options message. The name of the class is required in case + the options message is None and has to be created. + """ + self._options = options + self._options_class_name = options_class_name + self._serialized_options = serialized_options + + # Does this descriptor have non-default options? + self.has_options = (options is not None) or (serialized_options is not None) + + def _SetOptions(self, options, options_class_name): + """Sets the descriptor's options + + This function is used in generated proto2 files to update descriptor + options. It must not be used outside proto2. + """ + self._options = options + self._options_class_name = options_class_name + + # Does this descriptor have non-default options? + self.has_options = options is not None + + def GetOptions(self): + """Retrieves descriptor options. + + This method returns the options set or creates the default options for the + descriptor. + """ + if self._options: + return self._options + + from google.protobuf import descriptor_pb2 + try: + options_class = getattr(descriptor_pb2, + self._options_class_name) + except AttributeError: + raise RuntimeError('Unknown options class name %s!' % + (self._options_class_name)) + + with _lock: + if self._serialized_options is None: + self._options = options_class() + else: + self._options = _ParseOptions(options_class(), + self._serialized_options) + + return self._options + + +class _NestedDescriptorBase(DescriptorBase): + """Common class for descriptors that can be nested.""" + + def __init__(self, options, options_class_name, name, full_name, + file, containing_type, serialized_start=None, + serialized_end=None, serialized_options=None): + """Constructor. + + Args: + options: Protocol message options or None + to use default message options. + options_class_name (str): The class name of the above options. + name (str): Name of this protocol message type. + full_name (str): Fully-qualified name of this protocol message type, + which will include protocol "package" name and the name of any + enclosing types. + file (FileDescriptor): Reference to file info. + containing_type: if provided, this is a nested descriptor, with this + descriptor as parent, otherwise None. + serialized_start: The start index (inclusive) in block in the + file.serialized_pb that describes this descriptor. + serialized_end: The end index (exclusive) in block in the + file.serialized_pb that describes this descriptor. + serialized_options: Protocol message serialized options or None. + """ + super(_NestedDescriptorBase, self).__init__( + options, serialized_options, options_class_name) + + self.name = name + # TODO(falk): Add function to calculate full_name instead of having it in + # memory? + self.full_name = full_name + self.file = file + self.containing_type = containing_type + + self._serialized_start = serialized_start + self._serialized_end = serialized_end + + def CopyToProto(self, proto): + """Copies this to the matching proto in descriptor_pb2. + + Args: + proto: An empty proto instance from descriptor_pb2. + + Raises: + Error: If self couldn't be serialized, due to to few constructor + arguments. + """ + if (self.file is not None and + self._serialized_start is not None and + self._serialized_end is not None): + proto.ParseFromString(self.file.serialized_pb[ + self._serialized_start:self._serialized_end]) + else: + raise Error('Descriptor does not contain serialization.') + + +class Descriptor(_NestedDescriptorBase): + + """Descriptor for a protocol message type. + + Attributes: + name (str): Name of this protocol message type. + full_name (str): Fully-qualified name of this protocol message type, + which will include protocol "package" name and the name of any + enclosing types. + containing_type (Descriptor): Reference to the descriptor of the type + containing us, or None if this is top-level. + fields (list[FieldDescriptor]): Field descriptors for all fields in + this type. + fields_by_number (dict(int, FieldDescriptor)): Same + :class:`FieldDescriptor` objects as in :attr:`fields`, but indexed + by "number" attribute in each FieldDescriptor. + fields_by_name (dict(str, FieldDescriptor)): Same + :class:`FieldDescriptor` objects as in :attr:`fields`, but indexed by + "name" attribute in each :class:`FieldDescriptor`. + nested_types (list[Descriptor]): Descriptor references + for all protocol message types nested within this one. + nested_types_by_name (dict(str, Descriptor)): Same Descriptor + objects as in :attr:`nested_types`, but indexed by "name" attribute + in each Descriptor. + enum_types (list[EnumDescriptor]): :class:`EnumDescriptor` references + for all enums contained within this type. + enum_types_by_name (dict(str, EnumDescriptor)): Same + :class:`EnumDescriptor` objects as in :attr:`enum_types`, but + indexed by "name" attribute in each EnumDescriptor. + enum_values_by_name (dict(str, EnumValueDescriptor)): Dict mapping + from enum value name to :class:`EnumValueDescriptor` for that value. + extensions (list[FieldDescriptor]): All extensions defined directly + within this message type (NOT within a nested type). + extensions_by_name (dict(str, FieldDescriptor)): Same FieldDescriptor + objects as :attr:`extensions`, but indexed by "name" attribute of each + FieldDescriptor. + is_extendable (bool): Does this type define any extension ranges? + oneofs (list[OneofDescriptor]): The list of descriptors for oneof fields + in this message. + oneofs_by_name (dict(str, OneofDescriptor)): Same objects as in + :attr:`oneofs`, but indexed by "name" attribute. + file (FileDescriptor): Reference to file descriptor. + + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.Descriptor + + def __new__( + cls, + name=None, + full_name=None, + filename=None, + containing_type=None, + fields=None, + nested_types=None, + enum_types=None, + extensions=None, + options=None, + serialized_options=None, + is_extendable=True, + extension_ranges=None, + oneofs=None, + file=None, # pylint: disable=redefined-builtin + serialized_start=None, + serialized_end=None, + syntax=None, + create_key=None): + _message.Message._CheckCalledFromGeneratedFile() + return _message.default_pool.FindMessageTypeByName(full_name) + + # NOTE(tmarek): The file argument redefining a builtin is nothing we can + # fix right now since we don't know how many clients already rely on the + # name of the argument. + def __init__(self, name, full_name, filename, containing_type, fields, + nested_types, enum_types, extensions, options=None, + serialized_options=None, + is_extendable=True, extension_ranges=None, oneofs=None, + file=None, serialized_start=None, serialized_end=None, # pylint: disable=redefined-builtin + syntax=None, create_key=None): + """Arguments to __init__() are as described in the description + of Descriptor fields above. + + Note that filename is an obsolete argument, that is not used anymore. + Please use file.name to access this as an attribute. + """ + if create_key is not _internal_create_key: + _Deprecated('Descriptor') + + super(Descriptor, self).__init__( + options, 'MessageOptions', name, full_name, file, + containing_type, serialized_start=serialized_start, + serialized_end=serialized_end, serialized_options=serialized_options) + + # We have fields in addition to fields_by_name and fields_by_number, + # so that: + # 1. Clients can index fields by "order in which they're listed." + # 2. Clients can easily iterate over all fields with the terse + # syntax: for f in descriptor.fields: ... + self.fields = fields + for field in self.fields: + field.containing_type = self + self.fields_by_number = dict((f.number, f) for f in fields) + self.fields_by_name = dict((f.name, f) for f in fields) + self._fields_by_camelcase_name = None + + self.nested_types = nested_types + for nested_type in nested_types: + nested_type.containing_type = self + self.nested_types_by_name = dict((t.name, t) for t in nested_types) + + self.enum_types = enum_types + for enum_type in self.enum_types: + enum_type.containing_type = self + self.enum_types_by_name = dict((t.name, t) for t in enum_types) + self.enum_values_by_name = dict( + (v.name, v) for t in enum_types for v in t.values) + + self.extensions = extensions + for extension in self.extensions: + extension.extension_scope = self + self.extensions_by_name = dict((f.name, f) for f in extensions) + self.is_extendable = is_extendable + self.extension_ranges = extension_ranges + self.oneofs = oneofs if oneofs is not None else [] + self.oneofs_by_name = dict((o.name, o) for o in self.oneofs) + for oneof in self.oneofs: + oneof.containing_type = self + self.syntax = syntax or "proto2" + + @property + def fields_by_camelcase_name(self): + """Same FieldDescriptor objects as in :attr:`fields`, but indexed by + :attr:`FieldDescriptor.camelcase_name`. + """ + if self._fields_by_camelcase_name is None: + self._fields_by_camelcase_name = dict( + (f.camelcase_name, f) for f in self.fields) + return self._fields_by_camelcase_name + + def EnumValueName(self, enum, value): + """Returns the string name of an enum value. + + This is just a small helper method to simplify a common operation. + + Args: + enum: string name of the Enum. + value: int, value of the enum. + + Returns: + string name of the enum value. + + Raises: + KeyError if either the Enum doesn't exist or the value is not a valid + value for the enum. + """ + return self.enum_types_by_name[enum].values_by_number[value].name + + def CopyToProto(self, proto): + """Copies this to a descriptor_pb2.DescriptorProto. + + Args: + proto: An empty descriptor_pb2.DescriptorProto. + """ + # This function is overridden to give a better doc comment. + super(Descriptor, self).CopyToProto(proto) + + +# TODO(robinson): We should have aggressive checking here, +# for example: +# * If you specify a repeated field, you should not be allowed +# to specify a default value. +# * [Other examples here as needed]. +# +# TODO(robinson): for this and other *Descriptor classes, we +# might also want to lock things down aggressively (e.g., +# prevent clients from setting the attributes). Having +# stronger invariants here in general will reduce the number +# of runtime checks we must do in reflection.py... +class FieldDescriptor(DescriptorBase): + + """Descriptor for a single field in a .proto file. + + Attributes: + name (str): Name of this field, exactly as it appears in .proto. + full_name (str): Name of this field, including containing scope. This is + particularly relevant for extensions. + index (int): Dense, 0-indexed index giving the order that this + field textually appears within its message in the .proto file. + number (int): Tag number declared for this field in the .proto file. + + type (int): (One of the TYPE_* constants below) Declared type. + cpp_type (int): (One of the CPPTYPE_* constants below) C++ type used to + represent this field. + + label (int): (One of the LABEL_* constants below) Tells whether this + field is optional, required, or repeated. + has_default_value (bool): True if this field has a default value defined, + otherwise false. + default_value (Varies): Default value of this field. Only + meaningful for non-repeated scalar fields. Repeated fields + should always set this to [], and non-repeated composite + fields should always set this to None. + + containing_type (Descriptor): Descriptor of the protocol message + type that contains this field. Set by the Descriptor constructor + if we're passed into one. + Somewhat confusingly, for extension fields, this is the + descriptor of the EXTENDED message, not the descriptor + of the message containing this field. (See is_extension and + extension_scope below). + message_type (Descriptor): If a composite field, a descriptor + of the message type contained in this field. Otherwise, this is None. + enum_type (EnumDescriptor): If this field contains an enum, a + descriptor of that enum. Otherwise, this is None. + + is_extension: True iff this describes an extension field. + extension_scope (Descriptor): Only meaningful if is_extension is True. + Gives the message that immediately contains this extension field. + Will be None iff we're a top-level (file-level) extension field. + + options (descriptor_pb2.FieldOptions): Protocol message field options or + None to use default field options. + + containing_oneof (OneofDescriptor): If the field is a member of a oneof + union, contains its descriptor. Otherwise, None. + + file (FileDescriptor): Reference to file descriptor. + """ + + # Must be consistent with C++ FieldDescriptor::Type enum in + # descriptor.h. + # + # TODO(robinson): Find a way to eliminate this repetition. + TYPE_DOUBLE = 1 + TYPE_FLOAT = 2 + TYPE_INT64 = 3 + TYPE_UINT64 = 4 + TYPE_INT32 = 5 + TYPE_FIXED64 = 6 + TYPE_FIXED32 = 7 + TYPE_BOOL = 8 + TYPE_STRING = 9 + TYPE_GROUP = 10 + TYPE_MESSAGE = 11 + TYPE_BYTES = 12 + TYPE_UINT32 = 13 + TYPE_ENUM = 14 + TYPE_SFIXED32 = 15 + TYPE_SFIXED64 = 16 + TYPE_SINT32 = 17 + TYPE_SINT64 = 18 + MAX_TYPE = 18 + + # Must be consistent with C++ FieldDescriptor::CppType enum in + # descriptor.h. + # + # TODO(robinson): Find a way to eliminate this repetition. + CPPTYPE_INT32 = 1 + CPPTYPE_INT64 = 2 + CPPTYPE_UINT32 = 3 + CPPTYPE_UINT64 = 4 + CPPTYPE_DOUBLE = 5 + CPPTYPE_FLOAT = 6 + CPPTYPE_BOOL = 7 + CPPTYPE_ENUM = 8 + CPPTYPE_STRING = 9 + CPPTYPE_MESSAGE = 10 + MAX_CPPTYPE = 10 + + _PYTHON_TO_CPP_PROTO_TYPE_MAP = { + TYPE_DOUBLE: CPPTYPE_DOUBLE, + TYPE_FLOAT: CPPTYPE_FLOAT, + TYPE_ENUM: CPPTYPE_ENUM, + TYPE_INT64: CPPTYPE_INT64, + TYPE_SINT64: CPPTYPE_INT64, + TYPE_SFIXED64: CPPTYPE_INT64, + TYPE_UINT64: CPPTYPE_UINT64, + TYPE_FIXED64: CPPTYPE_UINT64, + TYPE_INT32: CPPTYPE_INT32, + TYPE_SFIXED32: CPPTYPE_INT32, + TYPE_SINT32: CPPTYPE_INT32, + TYPE_UINT32: CPPTYPE_UINT32, + TYPE_FIXED32: CPPTYPE_UINT32, + TYPE_BYTES: CPPTYPE_STRING, + TYPE_STRING: CPPTYPE_STRING, + TYPE_BOOL: CPPTYPE_BOOL, + TYPE_MESSAGE: CPPTYPE_MESSAGE, + TYPE_GROUP: CPPTYPE_MESSAGE + } + + # Must be consistent with C++ FieldDescriptor::Label enum in + # descriptor.h. + # + # TODO(robinson): Find a way to eliminate this repetition. + LABEL_OPTIONAL = 1 + LABEL_REQUIRED = 2 + LABEL_REPEATED = 3 + MAX_LABEL = 3 + + # Must be consistent with C++ constants kMaxNumber, kFirstReservedNumber, + # and kLastReservedNumber in descriptor.h + MAX_FIELD_NUMBER = (1 << 29) - 1 + FIRST_RESERVED_FIELD_NUMBER = 19000 + LAST_RESERVED_FIELD_NUMBER = 19999 + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.FieldDescriptor + + def __new__(cls, name, full_name, index, number, type, cpp_type, label, + default_value, message_type, enum_type, containing_type, + is_extension, extension_scope, options=None, + serialized_options=None, + has_default_value=True, containing_oneof=None, json_name=None, + file=None, create_key=None): # pylint: disable=redefined-builtin + _message.Message._CheckCalledFromGeneratedFile() + if is_extension: + return _message.default_pool.FindExtensionByName(full_name) + else: + return _message.default_pool.FindFieldByName(full_name) + + def __init__(self, name, full_name, index, number, type, cpp_type, label, + default_value, message_type, enum_type, containing_type, + is_extension, extension_scope, options=None, + serialized_options=None, + has_default_value=True, containing_oneof=None, json_name=None, + file=None, create_key=None): # pylint: disable=redefined-builtin + """The arguments are as described in the description of FieldDescriptor + attributes above. + + Note that containing_type may be None, and may be set later if necessary + (to deal with circular references between message types, for example). + Likewise for extension_scope. + """ + if create_key is not _internal_create_key: + _Deprecated('FieldDescriptor') + + super(FieldDescriptor, self).__init__( + options, serialized_options, 'FieldOptions') + self.name = name + self.full_name = full_name + self.file = file + self._camelcase_name = None + if json_name is None: + self.json_name = _ToJsonName(name) + else: + self.json_name = json_name + self.index = index + self.number = number + self.type = type + self.cpp_type = cpp_type + self.label = label + self.has_default_value = has_default_value + self.default_value = default_value + self.containing_type = containing_type + self.message_type = message_type + self.enum_type = enum_type + self.is_extension = is_extension + self.extension_scope = extension_scope + self.containing_oneof = containing_oneof + if api_implementation.Type() == 'cpp': + if is_extension: + self._cdescriptor = _message.default_pool.FindExtensionByName(full_name) + else: + self._cdescriptor = _message.default_pool.FindFieldByName(full_name) + else: + self._cdescriptor = None + + @property + def camelcase_name(self): + """Camelcase name of this field. + + Returns: + str: the name in CamelCase. + """ + if self._camelcase_name is None: + self._camelcase_name = _ToCamelCase(self.name) + return self._camelcase_name + + @property + def has_presence(self): + """Whether the field distinguishes between unpopulated and default values. + + Raises: + RuntimeError: singular field that is not linked with message nor file. + """ + if self.label == FieldDescriptor.LABEL_REPEATED: + return False + if (self.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE or + self.containing_oneof): + return True + if hasattr(self.file, 'syntax'): + return self.file.syntax == 'proto2' + if hasattr(self.message_type, 'syntax'): + return self.message_type.syntax == 'proto2' + raise RuntimeError( + 'has_presence is not ready to use because field %s is not' + ' linked with message type nor file' % self.full_name) + + @staticmethod + def ProtoTypeToCppProtoType(proto_type): + """Converts from a Python proto type to a C++ Proto Type. + + The Python ProtocolBuffer classes specify both the 'Python' datatype and the + 'C++' datatype - and they're not the same. This helper method should + translate from one to another. + + Args: + proto_type: the Python proto type (descriptor.FieldDescriptor.TYPE_*) + Returns: + int: descriptor.FieldDescriptor.CPPTYPE_*, the C++ type. + Raises: + TypeTransformationError: when the Python proto type isn't known. + """ + try: + return FieldDescriptor._PYTHON_TO_CPP_PROTO_TYPE_MAP[proto_type] + except KeyError: + raise TypeTransformationError('Unknown proto_type: %s' % proto_type) + + +class EnumDescriptor(_NestedDescriptorBase): + + """Descriptor for an enum defined in a .proto file. + + Attributes: + name (str): Name of the enum type. + full_name (str): Full name of the type, including package name + and any enclosing type(s). + + values (list[EnumValueDescriptor]): List of the values + in this enum. + values_by_name (dict(str, EnumValueDescriptor)): Same as :attr:`values`, + but indexed by the "name" field of each EnumValueDescriptor. + values_by_number (dict(int, EnumValueDescriptor)): Same as :attr:`values`, + but indexed by the "number" field of each EnumValueDescriptor. + containing_type (Descriptor): Descriptor of the immediate containing + type of this enum, or None if this is an enum defined at the + top level in a .proto file. Set by Descriptor's constructor + if we're passed into one. + file (FileDescriptor): Reference to file descriptor. + options (descriptor_pb2.EnumOptions): Enum options message or + None to use default enum options. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.EnumDescriptor + + def __new__(cls, name, full_name, filename, values, + containing_type=None, options=None, + serialized_options=None, file=None, # pylint: disable=redefined-builtin + serialized_start=None, serialized_end=None, create_key=None): + _message.Message._CheckCalledFromGeneratedFile() + return _message.default_pool.FindEnumTypeByName(full_name) + + def __init__(self, name, full_name, filename, values, + containing_type=None, options=None, + serialized_options=None, file=None, # pylint: disable=redefined-builtin + serialized_start=None, serialized_end=None, create_key=None): + """Arguments are as described in the attribute description above. + + Note that filename is an obsolete argument, that is not used anymore. + Please use file.name to access this as an attribute. + """ + if create_key is not _internal_create_key: + _Deprecated('EnumDescriptor') + + super(EnumDescriptor, self).__init__( + options, 'EnumOptions', name, full_name, file, + containing_type, serialized_start=serialized_start, + serialized_end=serialized_end, serialized_options=serialized_options) + + self.values = values + for value in self.values: + value.type = self + self.values_by_name = dict((v.name, v) for v in values) + # Values are reversed to ensure that the first alias is retained. + self.values_by_number = dict((v.number, v) for v in reversed(values)) + + def CopyToProto(self, proto): + """Copies this to a descriptor_pb2.EnumDescriptorProto. + + Args: + proto (descriptor_pb2.EnumDescriptorProto): An empty descriptor proto. + """ + # This function is overridden to give a better doc comment. + super(EnumDescriptor, self).CopyToProto(proto) + + +class EnumValueDescriptor(DescriptorBase): + + """Descriptor for a single value within an enum. + + Attributes: + name (str): Name of this value. + index (int): Dense, 0-indexed index giving the order that this + value appears textually within its enum in the .proto file. + number (int): Actual number assigned to this enum value. + type (EnumDescriptor): :class:`EnumDescriptor` to which this value + belongs. Set by :class:`EnumDescriptor`'s constructor if we're + passed into one. + options (descriptor_pb2.EnumValueOptions): Enum value options message or + None to use default enum value options options. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.EnumValueDescriptor + + def __new__(cls, name, index, number, + type=None, # pylint: disable=redefined-builtin + options=None, serialized_options=None, create_key=None): + _message.Message._CheckCalledFromGeneratedFile() + # There is no way we can build a complete EnumValueDescriptor with the + # given parameters (the name of the Enum is not known, for example). + # Fortunately generated files just pass it to the EnumDescriptor() + # constructor, which will ignore it, so returning None is good enough. + return None + + def __init__(self, name, index, number, + type=None, # pylint: disable=redefined-builtin + options=None, serialized_options=None, create_key=None): + """Arguments are as described in the attribute description above.""" + if create_key is not _internal_create_key: + _Deprecated('EnumValueDescriptor') + + super(EnumValueDescriptor, self).__init__( + options, serialized_options, 'EnumValueOptions') + self.name = name + self.index = index + self.number = number + self.type = type + + +class OneofDescriptor(DescriptorBase): + """Descriptor for a oneof field. + + Attributes: + name (str): Name of the oneof field. + full_name (str): Full name of the oneof field, including package name. + index (int): 0-based index giving the order of the oneof field inside + its containing type. + containing_type (Descriptor): :class:`Descriptor` of the protocol message + type that contains this field. Set by the :class:`Descriptor` constructor + if we're passed into one. + fields (list[FieldDescriptor]): The list of field descriptors this + oneof can contain. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.OneofDescriptor + + def __new__( + cls, name, full_name, index, containing_type, fields, options=None, + serialized_options=None, create_key=None): + _message.Message._CheckCalledFromGeneratedFile() + return _message.default_pool.FindOneofByName(full_name) + + def __init__( + self, name, full_name, index, containing_type, fields, options=None, + serialized_options=None, create_key=None): + """Arguments are as described in the attribute description above.""" + if create_key is not _internal_create_key: + _Deprecated('OneofDescriptor') + + super(OneofDescriptor, self).__init__( + options, serialized_options, 'OneofOptions') + self.name = name + self.full_name = full_name + self.index = index + self.containing_type = containing_type + self.fields = fields + + +class ServiceDescriptor(_NestedDescriptorBase): + + """Descriptor for a service. + + Attributes: + name (str): Name of the service. + full_name (str): Full name of the service, including package name. + index (int): 0-indexed index giving the order that this services + definition appears within the .proto file. + methods (list[MethodDescriptor]): List of methods provided by this + service. + methods_by_name (dict(str, MethodDescriptor)): Same + :class:`MethodDescriptor` objects as in :attr:`methods_by_name`, but + indexed by "name" attribute in each :class:`MethodDescriptor`. + options (descriptor_pb2.ServiceOptions): Service options message or + None to use default service options. + file (FileDescriptor): Reference to file info. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.ServiceDescriptor + + def __new__( + cls, + name=None, + full_name=None, + index=None, + methods=None, + options=None, + serialized_options=None, + file=None, # pylint: disable=redefined-builtin + serialized_start=None, + serialized_end=None, + create_key=None): + _message.Message._CheckCalledFromGeneratedFile() # pylint: disable=protected-access + return _message.default_pool.FindServiceByName(full_name) + + def __init__(self, name, full_name, index, methods, options=None, + serialized_options=None, file=None, # pylint: disable=redefined-builtin + serialized_start=None, serialized_end=None, create_key=None): + if create_key is not _internal_create_key: + _Deprecated('ServiceDescriptor') + + super(ServiceDescriptor, self).__init__( + options, 'ServiceOptions', name, full_name, file, + None, serialized_start=serialized_start, + serialized_end=serialized_end, serialized_options=serialized_options) + self.index = index + self.methods = methods + self.methods_by_name = dict((m.name, m) for m in methods) + # Set the containing service for each method in this service. + for method in self.methods: + method.containing_service = self + + def FindMethodByName(self, name): + """Searches for the specified method, and returns its descriptor. + + Args: + name (str): Name of the method. + Returns: + MethodDescriptor or None: the descriptor for the requested method, if + found. + """ + return self.methods_by_name.get(name, None) + + def CopyToProto(self, proto): + """Copies this to a descriptor_pb2.ServiceDescriptorProto. + + Args: + proto (descriptor_pb2.ServiceDescriptorProto): An empty descriptor proto. + """ + # This function is overridden to give a better doc comment. + super(ServiceDescriptor, self).CopyToProto(proto) + + +class MethodDescriptor(DescriptorBase): + + """Descriptor for a method in a service. + + Attributes: + name (str): Name of the method within the service. + full_name (str): Full name of method. + index (int): 0-indexed index of the method inside the service. + containing_service (ServiceDescriptor): The service that contains this + method. + input_type (Descriptor): The descriptor of the message that this method + accepts. + output_type (Descriptor): The descriptor of the message that this method + returns. + client_streaming (bool): Whether this method uses client streaming. + server_streaming (bool): Whether this method uses server streaming. + options (descriptor_pb2.MethodOptions or None): Method options message, or + None to use default method options. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.MethodDescriptor + + def __new__(cls, + name, + full_name, + index, + containing_service, + input_type, + output_type, + client_streaming=False, + server_streaming=False, + options=None, + serialized_options=None, + create_key=None): + _message.Message._CheckCalledFromGeneratedFile() # pylint: disable=protected-access + return _message.default_pool.FindMethodByName(full_name) + + def __init__(self, + name, + full_name, + index, + containing_service, + input_type, + output_type, + client_streaming=False, + server_streaming=False, + options=None, + serialized_options=None, + create_key=None): + """The arguments are as described in the description of MethodDescriptor + attributes above. + + Note that containing_service may be None, and may be set later if necessary. + """ + if create_key is not _internal_create_key: + _Deprecated('MethodDescriptor') + + super(MethodDescriptor, self).__init__( + options, serialized_options, 'MethodOptions') + self.name = name + self.full_name = full_name + self.index = index + self.containing_service = containing_service + self.input_type = input_type + self.output_type = output_type + self.client_streaming = client_streaming + self.server_streaming = server_streaming + + def CopyToProto(self, proto): + """Copies this to a descriptor_pb2.MethodDescriptorProto. + + Args: + proto (descriptor_pb2.MethodDescriptorProto): An empty descriptor proto. + + Raises: + Error: If self couldn't be serialized, due to too few constructor + arguments. + """ + if self.containing_service is not None: + from google.protobuf import descriptor_pb2 + service_proto = descriptor_pb2.ServiceDescriptorProto() + self.containing_service.CopyToProto(service_proto) + proto.CopyFrom(service_proto.method[self.index]) + else: + raise Error('Descriptor does not contain a service.') + + +class FileDescriptor(DescriptorBase): + """Descriptor for a file. Mimics the descriptor_pb2.FileDescriptorProto. + + Note that :attr:`enum_types_by_name`, :attr:`extensions_by_name`, and + :attr:`dependencies` fields are only set by the + :py:mod:`google.protobuf.message_factory` module, and not by the generated + proto code. + + Attributes: + name (str): Name of file, relative to root of source tree. + package (str): Name of the package + syntax (str): string indicating syntax of the file (can be "proto2" or + "proto3") + serialized_pb (bytes): Byte string of serialized + :class:`descriptor_pb2.FileDescriptorProto`. + dependencies (list[FileDescriptor]): List of other :class:`FileDescriptor` + objects this :class:`FileDescriptor` depends on. + public_dependencies (list[FileDescriptor]): A subset of + :attr:`dependencies`, which were declared as "public". + message_types_by_name (dict(str, Descriptor)): Mapping from message names + to their :class:`Descriptor`. + enum_types_by_name (dict(str, EnumDescriptor)): Mapping from enum names to + their :class:`EnumDescriptor`. + extensions_by_name (dict(str, FieldDescriptor)): Mapping from extension + names declared at file scope to their :class:`FieldDescriptor`. + services_by_name (dict(str, ServiceDescriptor)): Mapping from services' + names to their :class:`ServiceDescriptor`. + pool (DescriptorPool): The pool this descriptor belongs to. When not + passed to the constructor, the global default pool is used. + """ + + if _USE_C_DESCRIPTORS: + _C_DESCRIPTOR_CLASS = _message.FileDescriptor + + def __new__(cls, name, package, options=None, + serialized_options=None, serialized_pb=None, + dependencies=None, public_dependencies=None, + syntax=None, pool=None, create_key=None): + # FileDescriptor() is called from various places, not only from generated + # files, to register dynamic proto files and messages. + # pylint: disable=g-explicit-bool-comparison + if serialized_pb == b'': + # Cpp generated code must be linked in if serialized_pb is '' + try: + return _message.default_pool.FindFileByName(name) + except KeyError: + raise RuntimeError('Please link in cpp generated lib for %s' % (name)) + elif serialized_pb: + return _message.default_pool.AddSerializedFile(serialized_pb) + else: + return super(FileDescriptor, cls).__new__(cls) + + def __init__(self, name, package, options=None, + serialized_options=None, serialized_pb=None, + dependencies=None, public_dependencies=None, + syntax=None, pool=None, create_key=None): + """Constructor.""" + if create_key is not _internal_create_key: + _Deprecated('FileDescriptor') + + super(FileDescriptor, self).__init__( + options, serialized_options, 'FileOptions') + + if pool is None: + from google.protobuf import descriptor_pool + pool = descriptor_pool.Default() + self.pool = pool + self.message_types_by_name = {} + self.name = name + self.package = package + self.syntax = syntax or "proto2" + self.serialized_pb = serialized_pb + + self.enum_types_by_name = {} + self.extensions_by_name = {} + self.services_by_name = {} + self.dependencies = (dependencies or []) + self.public_dependencies = (public_dependencies or []) + + def CopyToProto(self, proto): + """Copies this to a descriptor_pb2.FileDescriptorProto. + + Args: + proto: An empty descriptor_pb2.FileDescriptorProto. + """ + proto.ParseFromString(self.serialized_pb) + + +def _ParseOptions(message, string): + """Parses serialized options. + + This helper function is used to parse serialized options in generated + proto2 files. It must not be used outside proto2. + """ + message.ParseFromString(string) + return message + + +def _ToCamelCase(name): + """Converts name to camel-case and returns it.""" + capitalize_next = False + result = [] + + for c in name: + if c == '_': + if result: + capitalize_next = True + elif capitalize_next: + result.append(c.upper()) + capitalize_next = False + else: + result += c + + # Lower-case the first letter. + if result and result[0].isupper(): + result[0] = result[0].lower() + return ''.join(result) + + +def _OptionsOrNone(descriptor_proto): + """Returns the value of the field `options`, or None if it is not set.""" + if descriptor_proto.HasField('options'): + return descriptor_proto.options + else: + return None + + +def _ToJsonName(name): + """Converts name to Json name and returns it.""" + capitalize_next = False + result = [] + + for c in name: + if c == '_': + capitalize_next = True + elif capitalize_next: + result.append(c.upper()) + capitalize_next = False + else: + result += c + + return ''.join(result) + + +def MakeDescriptor(desc_proto, package='', build_file_if_cpp=True, + syntax=None): + """Make a protobuf Descriptor given a DescriptorProto protobuf. + + Handles nested descriptors. Note that this is limited to the scope of defining + a message inside of another message. Composite fields can currently only be + resolved if the message is defined in the same scope as the field. + + Args: + desc_proto: The descriptor_pb2.DescriptorProto protobuf message. + package: Optional package name for the new message Descriptor (string). + build_file_if_cpp: Update the C++ descriptor pool if api matches. + Set to False on recursion, so no duplicates are created. + syntax: The syntax/semantics that should be used. Set to "proto3" to get + proto3 field presence semantics. + Returns: + A Descriptor for protobuf messages. + """ + if api_implementation.Type() == 'cpp' and build_file_if_cpp: + # The C++ implementation requires all descriptors to be backed by the same + # definition in the C++ descriptor pool. To do this, we build a + # FileDescriptorProto with the same definition as this descriptor and build + # it into the pool. + from google.protobuf import descriptor_pb2 + file_descriptor_proto = descriptor_pb2.FileDescriptorProto() + file_descriptor_proto.message_type.add().MergeFrom(desc_proto) + + # Generate a random name for this proto file to prevent conflicts with any + # imported ones. We need to specify a file name so the descriptor pool + # accepts our FileDescriptorProto, but it is not important what that file + # name is actually set to. + proto_name = binascii.hexlify(os.urandom(16)).decode('ascii') + + if package: + file_descriptor_proto.name = os.path.join(package.replace('.', '/'), + proto_name + '.proto') + file_descriptor_proto.package = package + else: + file_descriptor_proto.name = proto_name + '.proto' + + _message.default_pool.Add(file_descriptor_proto) + result = _message.default_pool.FindFileByName(file_descriptor_proto.name) + + if _USE_C_DESCRIPTORS: + return result.message_types_by_name[desc_proto.name] + + full_message_name = [desc_proto.name] + if package: full_message_name.insert(0, package) + + # Create Descriptors for enum types + enum_types = {} + for enum_proto in desc_proto.enum_type: + full_name = '.'.join(full_message_name + [enum_proto.name]) + enum_desc = EnumDescriptor( + enum_proto.name, full_name, None, [ + EnumValueDescriptor(enum_val.name, ii, enum_val.number, + create_key=_internal_create_key) + for ii, enum_val in enumerate(enum_proto.value)], + create_key=_internal_create_key) + enum_types[full_name] = enum_desc + + # Create Descriptors for nested types + nested_types = {} + for nested_proto in desc_proto.nested_type: + full_name = '.'.join(full_message_name + [nested_proto.name]) + # Nested types are just those defined inside of the message, not all types + # used by fields in the message, so no loops are possible here. + nested_desc = MakeDescriptor(nested_proto, + package='.'.join(full_message_name), + build_file_if_cpp=False, + syntax=syntax) + nested_types[full_name] = nested_desc + + fields = [] + for field_proto in desc_proto.field: + full_name = '.'.join(full_message_name + [field_proto.name]) + enum_desc = None + nested_desc = None + if field_proto.json_name: + json_name = field_proto.json_name + else: + json_name = None + if field_proto.HasField('type_name'): + type_name = field_proto.type_name + full_type_name = '.'.join(full_message_name + + [type_name[type_name.rfind('.')+1:]]) + if full_type_name in nested_types: + nested_desc = nested_types[full_type_name] + elif full_type_name in enum_types: + enum_desc = enum_types[full_type_name] + # Else type_name references a non-local type, which isn't implemented + field = FieldDescriptor( + field_proto.name, full_name, field_proto.number - 1, + field_proto.number, field_proto.type, + FieldDescriptor.ProtoTypeToCppProtoType(field_proto.type), + field_proto.label, None, nested_desc, enum_desc, None, False, None, + options=_OptionsOrNone(field_proto), has_default_value=False, + json_name=json_name, create_key=_internal_create_key) + fields.append(field) + + desc_name = '.'.join(full_message_name) + return Descriptor(desc_proto.name, desc_name, None, None, fields, + list(nested_types.values()), list(enum_types.values()), [], + options=_OptionsOrNone(desc_proto), + create_key=_internal_create_key) diff --git a/lib/protobuf/descriptor_database.py b/lib/protobuf/descriptor_database.py new file mode 100644 index 0000000..073eddc --- /dev/null +++ b/lib/protobuf/descriptor_database.py @@ -0,0 +1,177 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Provides a container for DescriptorProtos.""" + +__author__ = 'matthewtoia@google.com (Matt Toia)' + +import warnings + + +class Error(Exception): + pass + + +class DescriptorDatabaseConflictingDefinitionError(Error): + """Raised when a proto is added with the same name & different descriptor.""" + + +class DescriptorDatabase(object): + """A container accepting FileDescriptorProtos and maps DescriptorProtos.""" + + def __init__(self): + self._file_desc_protos_by_file = {} + self._file_desc_protos_by_symbol = {} + + def Add(self, file_desc_proto): + """Adds the FileDescriptorProto and its types to this database. + + Args: + file_desc_proto: The FileDescriptorProto to add. + Raises: + DescriptorDatabaseConflictingDefinitionError: if an attempt is made to + add a proto with the same name but different definition than an + existing proto in the database. + """ + proto_name = file_desc_proto.name + if proto_name not in self._file_desc_protos_by_file: + self._file_desc_protos_by_file[proto_name] = file_desc_proto + elif self._file_desc_protos_by_file[proto_name] != file_desc_proto: + raise DescriptorDatabaseConflictingDefinitionError( + '%s already added, but with different descriptor.' % proto_name) + else: + return + + # Add all the top-level descriptors to the index. + package = file_desc_proto.package + for message in file_desc_proto.message_type: + for name in _ExtractSymbols(message, package): + self._AddSymbol(name, file_desc_proto) + for enum in file_desc_proto.enum_type: + self._AddSymbol(('.'.join((package, enum.name))), file_desc_proto) + for enum_value in enum.value: + self._file_desc_protos_by_symbol[ + '.'.join((package, enum_value.name))] = file_desc_proto + for extension in file_desc_proto.extension: + self._AddSymbol(('.'.join((package, extension.name))), file_desc_proto) + for service in file_desc_proto.service: + self._AddSymbol(('.'.join((package, service.name))), file_desc_proto) + + def FindFileByName(self, name): + """Finds the file descriptor proto by file name. + + Typically the file name is a relative path ending to a .proto file. The + proto with the given name will have to have been added to this database + using the Add method or else an error will be raised. + + Args: + name: The file name to find. + + Returns: + The file descriptor proto matching the name. + + Raises: + KeyError if no file by the given name was added. + """ + + return self._file_desc_protos_by_file[name] + + def FindFileContainingSymbol(self, symbol): + """Finds the file descriptor proto containing the specified symbol. + + The symbol should be a fully qualified name including the file descriptor's + package and any containing messages. Some examples: + + 'some.package.name.Message' + 'some.package.name.Message.NestedEnum' + 'some.package.name.Message.some_field' + + The file descriptor proto containing the specified symbol must be added to + this database using the Add method or else an error will be raised. + + Args: + symbol: The fully qualified symbol name. + + Returns: + The file descriptor proto containing the symbol. + + Raises: + KeyError if no file contains the specified symbol. + """ + try: + return self._file_desc_protos_by_symbol[symbol] + except KeyError: + # Fields, enum values, and nested extensions are not in + # _file_desc_protos_by_symbol. Try to find the top level + # descriptor. Non-existent nested symbol under a valid top level + # descriptor can also be found. The behavior is the same with + # protobuf C++. + top_level, _, _ = symbol.rpartition('.') + try: + return self._file_desc_protos_by_symbol[top_level] + except KeyError: + # Raise the original symbol as a KeyError for better diagnostics. + raise KeyError(symbol) + + def FindFileContainingExtension(self, extendee_name, extension_number): + # TODO(jieluo): implement this API. + return None + + def FindAllExtensionNumbers(self, extendee_name): + # TODO(jieluo): implement this API. + return [] + + def _AddSymbol(self, name, file_desc_proto): + if name in self._file_desc_protos_by_symbol: + warn_msg = ('Conflict register for file "' + file_desc_proto.name + + '": ' + name + + ' is already defined in file "' + + self._file_desc_protos_by_symbol[name].name + '"') + warnings.warn(warn_msg, RuntimeWarning) + self._file_desc_protos_by_symbol[name] = file_desc_proto + + +def _ExtractSymbols(desc_proto, package): + """Pulls out all the symbols from a descriptor proto. + + Args: + desc_proto: The proto to extract symbols from. + package: The package containing the descriptor type. + + Yields: + The fully qualified name found in the descriptor. + """ + message_name = package + '.' + desc_proto.name if package else desc_proto.name + yield message_name + for nested_type in desc_proto.nested_type: + for symbol in _ExtractSymbols(nested_type, message_name): + yield symbol + for enum_type in desc_proto.enum_type: + yield '.'.join((message_name, enum_type.name)) diff --git a/lib/protobuf/descriptor_pb2.py b/lib/protobuf/descriptor_pb2.py new file mode 100644 index 0000000..f570386 --- /dev/null +++ b/lib/protobuf/descriptor_pb2.py @@ -0,0 +1,1925 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/descriptor.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() + + + + +if _descriptor._USE_C_DESCRIPTORS == False: + DESCRIPTOR = _descriptor.FileDescriptor( + name='google/protobuf/descriptor.proto', + package='google.protobuf', + syntax='proto2', + serialized_options=None, + create_key=_descriptor._internal_create_key, + serialized_pb=b'\n google/protobuf/descriptor.proto\x12\x0fgoogle.protobuf\"G\n\x11\x46ileDescriptorSet\x12\x32\n\x04\x66ile\x18\x01 \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\"\xdb\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12\x36\n\x0cmessage_type\x18\x04 \x03(\x0b\x32 .google.protobuf.DescriptorProto\x12\x37\n\tenum_type\x18\x05 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProto\x12\x38\n\x07service\x18\x06 \x03(\x0b\x32\'.google.protobuf.ServiceDescriptorProto\x12\x38\n\textension\x18\x07 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12-\n\x07options\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.FileOptions\x12\x39\n\x10source_code_info\x18\t \x01(\x0b\x32\x1f.google.protobuf.SourceCodeInfo\x12\x0e\n\x06syntax\x18\x0c \x01(\t\"\xa9\x05\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x34\n\x05\x66ield\x18\x02 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12\x38\n\textension\x18\x06 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12\x35\n\x0bnested_type\x18\x03 \x03(\x0b\x32 .google.protobuf.DescriptorProto\x12\x37\n\tenum_type\x18\x04 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProto\x12H\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32/.google.protobuf.DescriptorProto.ExtensionRange\x12\x39\n\noneof_decl\x18\x08 \x03(\x0b\x32%.google.protobuf.OneofDescriptorProto\x12\x30\n\x07options\x18\x07 \x01(\x0b\x32\x1f.google.protobuf.MessageOptions\x12\x46\n\x0ereserved_range\x18\t \x03(\x0b\x32..google.protobuf.DescriptorProto.ReservedRange\x12\x15\n\rreserved_name\x18\n \x03(\t\x1a\x65\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\x12\x37\n\x07options\x18\x03 \x01(\x0b\x32&.google.protobuf.ExtensionRangeOptions\x1a+\n\rReservedRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"g\n\x15\x45xtensionRangeOptions\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd5\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12:\n\x05label\x18\x04 \x01(\x0e\x32+.google.protobuf.FieldDescriptorProto.Label\x12\x38\n\x04type\x18\x05 \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12\x11\n\tjson_name\x18\n \x01(\t\x12.\n\x07options\x18\x08 \x01(\x0b\x32\x1d.google.protobuf.FieldOptions\x12\x17\n\x0fproto3_optional\x18\x11 \x01(\x08\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"T\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12.\n\x07options\x18\x02 \x01(\x0b\x32\x1d.google.protobuf.OneofOptions\"\xa4\x02\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x38\n\x05value\x18\x02 \x03(\x0b\x32).google.protobuf.EnumValueDescriptorProto\x12-\n\x07options\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.EnumOptions\x12N\n\x0ereserved_range\x18\x04 \x03(\x0b\x32\x36.google.protobuf.EnumDescriptorProto.EnumReservedRange\x12\x15\n\rreserved_name\x18\x05 \x03(\t\x1a/\n\x11\x45numReservedRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"l\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12\x32\n\x07options\x18\x03 \x01(\x0b\x32!.google.protobuf.EnumValueOptions\"\x90\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x36\n\x06method\x18\x02 \x03(\x0b\x32&.google.protobuf.MethodDescriptorProto\x12\x30\n\x07options\x18\x03 \x01(\x0b\x32\x1f.google.protobuf.ServiceOptions\"\xc1\x01\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12/\n\x07options\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.MethodOptions\x12\x1f\n\x10\x63lient_streaming\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10server_streaming\x18\x06 \x01(\x08:\x05\x66\x61lse\"\xa5\x06\n\x0b\x46ileOptions\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12)\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08\x42\x02\x18\x01\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12\x46\n\x0coptimize_for\x18\t \x01(\x0e\x32).google.protobuf.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12#\n\x14php_generic_services\x18* \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x10\x63\x63_enable_arenas\x18\x1f \x01(\x08:\x04true\x12\x19\n\x11objc_class_prefix\x18$ \x01(\t\x12\x18\n\x10\x63sharp_namespace\x18% \x01(\t\x12\x14\n\x0cswift_prefix\x18\' \x01(\t\x12\x18\n\x10php_class_prefix\x18( \x01(\t\x12\x15\n\rphp_namespace\x18) \x01(\t\x12\x1e\n\x16php_metadata_namespace\x18, \x01(\t\x12\x14\n\x0cruby_package\x18- \x01(\t\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08&\x10\'\"\x84\x02\n\x0eMessageOptions\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x11\n\tmap_entry\x18\x07 \x01(\x08\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07J\x04\x08\x08\x10\tJ\x04\x08\t\x10\n\"\xbe\x03\n\x0c\x46ieldOptions\x12:\n\x05\x63type\x18\x01 \x01(\x0e\x32#.google.protobuf.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12?\n\x06jstype\x18\x06 \x01(\x0e\x32$.google.protobuf.FieldOptions.JSType:\tJS_NORMAL\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x0funverified_lazy\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05\"^\n\x0cOneofOptions\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x93\x01\n\x0b\x45numOptions\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x05\x10\x06\"}\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"{\n\x0eServiceOptions\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xad\x02\n\rMethodOptions\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12_\n\x11idempotency_level\x18\" \x01(\x0e\x32/.google.protobuf.MethodOptions.IdempotencyLevel:\x13IDEMPOTENCY_UNKNOWN\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\"P\n\x10IdempotencyLevel\x12\x17\n\x13IDEMPOTENCY_UNKNOWN\x10\x00\x12\x13\n\x0fNO_SIDE_EFFECTS\x10\x01\x12\x0e\n\nIDEMPOTENT\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9e\x02\n\x13UninterpretedOption\x12;\n\x04name\x18\x02 \x03(\x0b\x32-.google.protobuf.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xd5\x01\n\x0eSourceCodeInfo\x12:\n\x08location\x18\x01 \x03(\x0b\x32(.google.protobuf.SourceCodeInfo.Location\x1a\x86\x01\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\t\x12!\n\x19leading_detached_comments\x18\x06 \x03(\t\"\xa7\x01\n\x11GeneratedCodeInfo\x12\x41\n\nannotation\x18\x01 \x03(\x0b\x32-.google.protobuf.GeneratedCodeInfo.Annotation\x1aO\n\nAnnotation\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x13\n\x0bsource_file\x18\x02 \x01(\t\x12\r\n\x05\x62\x65gin\x18\x03 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x04 \x01(\x05\x42~\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01Z-google.golang.org/protobuf/types/descriptorpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1aGoogle.Protobuf.Reflection' + ) +else: + DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n google/protobuf/descriptor.proto\x12\x0fgoogle.protobuf\"G\n\x11\x46ileDescriptorSet\x12\x32\n\x04\x66ile\x18\x01 \x03(\x0b\x32$.google.protobuf.FileDescriptorProto\"\xdb\x03\n\x13\x46ileDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0f\n\x07package\x18\x02 \x01(\t\x12\x12\n\ndependency\x18\x03 \x03(\t\x12\x19\n\x11public_dependency\x18\n \x03(\x05\x12\x17\n\x0fweak_dependency\x18\x0b \x03(\x05\x12\x36\n\x0cmessage_type\x18\x04 \x03(\x0b\x32 .google.protobuf.DescriptorProto\x12\x37\n\tenum_type\x18\x05 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProto\x12\x38\n\x07service\x18\x06 \x03(\x0b\x32\'.google.protobuf.ServiceDescriptorProto\x12\x38\n\textension\x18\x07 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12-\n\x07options\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.FileOptions\x12\x39\n\x10source_code_info\x18\t \x01(\x0b\x32\x1f.google.protobuf.SourceCodeInfo\x12\x0e\n\x06syntax\x18\x0c \x01(\t\"\xa9\x05\n\x0f\x44\x65scriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x34\n\x05\x66ield\x18\x02 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12\x38\n\textension\x18\x06 \x03(\x0b\x32%.google.protobuf.FieldDescriptorProto\x12\x35\n\x0bnested_type\x18\x03 \x03(\x0b\x32 .google.protobuf.DescriptorProto\x12\x37\n\tenum_type\x18\x04 \x03(\x0b\x32$.google.protobuf.EnumDescriptorProto\x12H\n\x0f\x65xtension_range\x18\x05 \x03(\x0b\x32/.google.protobuf.DescriptorProto.ExtensionRange\x12\x39\n\noneof_decl\x18\x08 \x03(\x0b\x32%.google.protobuf.OneofDescriptorProto\x12\x30\n\x07options\x18\x07 \x01(\x0b\x32\x1f.google.protobuf.MessageOptions\x12\x46\n\x0ereserved_range\x18\t \x03(\x0b\x32..google.protobuf.DescriptorProto.ReservedRange\x12\x15\n\rreserved_name\x18\n \x03(\t\x1a\x65\n\x0e\x45xtensionRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\x12\x37\n\x07options\x18\x03 \x01(\x0b\x32&.google.protobuf.ExtensionRangeOptions\x1a+\n\rReservedRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"g\n\x15\x45xtensionRangeOptions\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xd5\x05\n\x14\x46ieldDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12:\n\x05label\x18\x04 \x01(\x0e\x32+.google.protobuf.FieldDescriptorProto.Label\x12\x38\n\x04type\x18\x05 \x01(\x0e\x32*.google.protobuf.FieldDescriptorProto.Type\x12\x11\n\ttype_name\x18\x06 \x01(\t\x12\x10\n\x08\x65xtendee\x18\x02 \x01(\t\x12\x15\n\rdefault_value\x18\x07 \x01(\t\x12\x13\n\x0boneof_index\x18\t \x01(\x05\x12\x11\n\tjson_name\x18\n \x01(\t\x12.\n\x07options\x18\x08 \x01(\x0b\x32\x1d.google.protobuf.FieldOptions\x12\x17\n\x0fproto3_optional\x18\x11 \x01(\x08\"\xb6\x02\n\x04Type\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"C\n\x05Label\x12\x12\n\x0eLABEL_OPTIONAL\x10\x01\x12\x12\n\x0eLABEL_REQUIRED\x10\x02\x12\x12\n\x0eLABEL_REPEATED\x10\x03\"T\n\x14OneofDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12.\n\x07options\x18\x02 \x01(\x0b\x32\x1d.google.protobuf.OneofOptions\"\xa4\x02\n\x13\x45numDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x38\n\x05value\x18\x02 \x03(\x0b\x32).google.protobuf.EnumValueDescriptorProto\x12-\n\x07options\x18\x03 \x01(\x0b\x32\x1c.google.protobuf.EnumOptions\x12N\n\x0ereserved_range\x18\x04 \x03(\x0b\x32\x36.google.protobuf.EnumDescriptorProto.EnumReservedRange\x12\x15\n\rreserved_name\x18\x05 \x03(\t\x1a/\n\x11\x45numReservedRange\x12\r\n\x05start\x18\x01 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x02 \x01(\x05\"l\n\x18\x45numValueDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12\x32\n\x07options\x18\x03 \x01(\x0b\x32!.google.protobuf.EnumValueOptions\"\x90\x01\n\x16ServiceDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x36\n\x06method\x18\x02 \x03(\x0b\x32&.google.protobuf.MethodDescriptorProto\x12\x30\n\x07options\x18\x03 \x01(\x0b\x32\x1f.google.protobuf.ServiceOptions\"\xc1\x01\n\x15MethodDescriptorProto\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x12\n\ninput_type\x18\x02 \x01(\t\x12\x13\n\x0boutput_type\x18\x03 \x01(\t\x12/\n\x07options\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.MethodOptions\x12\x1f\n\x10\x63lient_streaming\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1f\n\x10server_streaming\x18\x06 \x01(\x08:\x05\x66\x61lse\"\xa5\x06\n\x0b\x46ileOptions\x12\x14\n\x0cjava_package\x18\x01 \x01(\t\x12\x1c\n\x14java_outer_classname\x18\x08 \x01(\t\x12\"\n\x13java_multiple_files\x18\n \x01(\x08:\x05\x66\x61lse\x12)\n\x1djava_generate_equals_and_hash\x18\x14 \x01(\x08\x42\x02\x18\x01\x12%\n\x16java_string_check_utf8\x18\x1b \x01(\x08:\x05\x66\x61lse\x12\x46\n\x0coptimize_for\x18\t \x01(\x0e\x32).google.protobuf.FileOptions.OptimizeMode:\x05SPEED\x12\x12\n\ngo_package\x18\x0b \x01(\t\x12\"\n\x13\x63\x63_generic_services\x18\x10 \x01(\x08:\x05\x66\x61lse\x12$\n\x15java_generic_services\x18\x11 \x01(\x08:\x05\x66\x61lse\x12\"\n\x13py_generic_services\x18\x12 \x01(\x08:\x05\x66\x61lse\x12#\n\x14php_generic_services\x18* \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x17 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x10\x63\x63_enable_arenas\x18\x1f \x01(\x08:\x04true\x12\x19\n\x11objc_class_prefix\x18$ \x01(\t\x12\x18\n\x10\x63sharp_namespace\x18% \x01(\t\x12\x14\n\x0cswift_prefix\x18\' \x01(\t\x12\x18\n\x10php_class_prefix\x18( \x01(\t\x12\x15\n\rphp_namespace\x18) \x01(\t\x12\x1e\n\x16php_metadata_namespace\x18, \x01(\t\x12\x14\n\x0cruby_package\x18- \x01(\t\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\":\n\x0cOptimizeMode\x12\t\n\x05SPEED\x10\x01\x12\r\n\tCODE_SIZE\x10\x02\x12\x10\n\x0cLITE_RUNTIME\x10\x03*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08&\x10\'\"\x84\x02\n\x0eMessageOptions\x12&\n\x17message_set_wire_format\x18\x01 \x01(\x08:\x05\x66\x61lse\x12.\n\x1fno_standard_descriptor_accessor\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x11\n\tmap_entry\x18\x07 \x01(\x08\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05J\x04\x08\x05\x10\x06J\x04\x08\x06\x10\x07J\x04\x08\x08\x10\tJ\x04\x08\t\x10\n\"\xbe\x03\n\x0c\x46ieldOptions\x12:\n\x05\x63type\x18\x01 \x01(\x0e\x32#.google.protobuf.FieldOptions.CType:\x06STRING\x12\x0e\n\x06packed\x18\x02 \x01(\x08\x12?\n\x06jstype\x18\x06 \x01(\x0e\x32$.google.protobuf.FieldOptions.JSType:\tJS_NORMAL\x12\x13\n\x04lazy\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x1e\n\x0funverified_lazy\x18\x0f \x01(\x08:\x05\x66\x61lse\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x13\n\x04weak\x18\n \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\"/\n\x05\x43Type\x12\n\n\x06STRING\x10\x00\x12\x08\n\x04\x43ORD\x10\x01\x12\x10\n\x0cSTRING_PIECE\x10\x02\"5\n\x06JSType\x12\r\n\tJS_NORMAL\x10\x00\x12\r\n\tJS_STRING\x10\x01\x12\r\n\tJS_NUMBER\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x04\x10\x05\"^\n\x0cOneofOptions\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x93\x01\n\x0b\x45numOptions\x12\x13\n\x0b\x61llow_alias\x18\x02 \x01(\x08\x12\x19\n\ndeprecated\x18\x03 \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02J\x04\x08\x05\x10\x06\"}\n\x10\x45numValueOptions\x12\x19\n\ndeprecated\x18\x01 \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"{\n\x0eServiceOptions\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\xad\x02\n\rMethodOptions\x12\x19\n\ndeprecated\x18! \x01(\x08:\x05\x66\x61lse\x12_\n\x11idempotency_level\x18\" \x01(\x0e\x32/.google.protobuf.MethodOptions.IdempotencyLevel:\x13IDEMPOTENCY_UNKNOWN\x12\x43\n\x14uninterpreted_option\x18\xe7\x07 \x03(\x0b\x32$.google.protobuf.UninterpretedOption\"P\n\x10IdempotencyLevel\x12\x17\n\x13IDEMPOTENCY_UNKNOWN\x10\x00\x12\x13\n\x0fNO_SIDE_EFFECTS\x10\x01\x12\x0e\n\nIDEMPOTENT\x10\x02*\t\x08\xe8\x07\x10\x80\x80\x80\x80\x02\"\x9e\x02\n\x13UninterpretedOption\x12;\n\x04name\x18\x02 \x03(\x0b\x32-.google.protobuf.UninterpretedOption.NamePart\x12\x18\n\x10identifier_value\x18\x03 \x01(\t\x12\x1a\n\x12positive_int_value\x18\x04 \x01(\x04\x12\x1a\n\x12negative_int_value\x18\x05 \x01(\x03\x12\x14\n\x0c\x64ouble_value\x18\x06 \x01(\x01\x12\x14\n\x0cstring_value\x18\x07 \x01(\x0c\x12\x17\n\x0f\x61ggregate_value\x18\x08 \x01(\t\x1a\x33\n\x08NamePart\x12\x11\n\tname_part\x18\x01 \x02(\t\x12\x14\n\x0cis_extension\x18\x02 \x02(\x08\"\xd5\x01\n\x0eSourceCodeInfo\x12:\n\x08location\x18\x01 \x03(\x0b\x32(.google.protobuf.SourceCodeInfo.Location\x1a\x86\x01\n\x08Location\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x10\n\x04span\x18\x02 \x03(\x05\x42\x02\x10\x01\x12\x18\n\x10leading_comments\x18\x03 \x01(\t\x12\x19\n\x11trailing_comments\x18\x04 \x01(\t\x12!\n\x19leading_detached_comments\x18\x06 \x03(\t\"\xa7\x01\n\x11GeneratedCodeInfo\x12\x41\n\nannotation\x18\x01 \x03(\x0b\x32-.google.protobuf.GeneratedCodeInfo.Annotation\x1aO\n\nAnnotation\x12\x10\n\x04path\x18\x01 \x03(\x05\x42\x02\x10\x01\x12\x13\n\x0bsource_file\x18\x02 \x01(\t\x12\r\n\x05\x62\x65gin\x18\x03 \x01(\x05\x12\x0b\n\x03\x65nd\x18\x04 \x01(\x05\x42~\n\x13\x63om.google.protobufB\x10\x44\x65scriptorProtosH\x01Z-google.golang.org/protobuf/types/descriptorpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1aGoogle.Protobuf.Reflection') + +if _descriptor._USE_C_DESCRIPTORS == False: + _FIELDDESCRIPTORPROTO_TYPE = _descriptor.EnumDescriptor( + name='Type', + full_name='google.protobuf.FieldDescriptorProto.Type', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='TYPE_DOUBLE', index=0, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_FLOAT', index=1, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_INT64', index=2, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_UINT64', index=3, number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_INT32', index=4, number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_FIXED64', index=5, number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_FIXED32', index=6, number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_BOOL', index=7, number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_STRING', index=8, number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_GROUP', index=9, number=10, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_MESSAGE', index=10, number=11, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_BYTES', index=11, number=12, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_UINT32', index=12, number=13, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_ENUM', index=13, number=14, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SFIXED32', index=14, number=15, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SFIXED64', index=15, number=16, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SINT32', index=16, number=17, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TYPE_SINT64', index=17, number=18, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDDESCRIPTORPROTO_TYPE) + + _FIELDDESCRIPTORPROTO_LABEL = _descriptor.EnumDescriptor( + name='Label', + full_name='google.protobuf.FieldDescriptorProto.Label', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='LABEL_OPTIONAL', index=0, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LABEL_REQUIRED', index=1, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LABEL_REPEATED', index=2, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDDESCRIPTORPROTO_LABEL) + + _FILEOPTIONS_OPTIMIZEMODE = _descriptor.EnumDescriptor( + name='OptimizeMode', + full_name='google.protobuf.FileOptions.OptimizeMode', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SPEED', index=0, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CODE_SIZE', index=1, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LITE_RUNTIME', index=2, number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FILEOPTIONS_OPTIMIZEMODE) + + _FIELDOPTIONS_CTYPE = _descriptor.EnumDescriptor( + name='CType', + full_name='google.protobuf.FieldOptions.CType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='STRING', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CORD', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STRING_PIECE', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_CTYPE) + + _FIELDOPTIONS_JSTYPE = _descriptor.EnumDescriptor( + name='JSType', + full_name='google.protobuf.FieldOptions.JSType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='JS_NORMAL', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='JS_STRING', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='JS_NUMBER', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_FIELDOPTIONS_JSTYPE) + + _METHODOPTIONS_IDEMPOTENCYLEVEL = _descriptor.EnumDescriptor( + name='IdempotencyLevel', + full_name='google.protobuf.MethodOptions.IdempotencyLevel', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='IDEMPOTENCY_UNKNOWN', index=0, number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='NO_SIDE_EFFECTS', index=1, number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='IDEMPOTENT', index=2, number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + ) + _sym_db.RegisterEnumDescriptor(_METHODOPTIONS_IDEMPOTENCYLEVEL) + + + _FILEDESCRIPTORSET = _descriptor.Descriptor( + name='FileDescriptorSet', + full_name='google.protobuf.FileDescriptorSet', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='file', full_name='google.protobuf.FileDescriptorSet.file', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _FILEDESCRIPTORPROTO = _descriptor.Descriptor( + name='FileDescriptorProto', + full_name='google.protobuf.FileDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.FileDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='package', full_name='google.protobuf.FileDescriptorProto.package', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='dependency', full_name='google.protobuf.FileDescriptorProto.dependency', index=2, + number=3, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='public_dependency', full_name='google.protobuf.FileDescriptorProto.public_dependency', index=3, + number=10, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='weak_dependency', full_name='google.protobuf.FileDescriptorProto.weak_dependency', index=4, + number=11, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='message_type', full_name='google.protobuf.FileDescriptorProto.message_type', index=5, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='enum_type', full_name='google.protobuf.FileDescriptorProto.enum_type', index=6, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='service', full_name='google.protobuf.FileDescriptorProto.service', index=7, + number=6, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='google.protobuf.FileDescriptorProto.extension', index=8, + number=7, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.FileDescriptorProto.options', index=9, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='source_code_info', full_name='google.protobuf.FileDescriptorProto.source_code_info', index=10, + number=9, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='syntax', full_name='google.protobuf.FileDescriptorProto.syntax', index=11, + number=12, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _DESCRIPTORPROTO_EXTENSIONRANGE = _descriptor.Descriptor( + name='ExtensionRange', + full_name='google.protobuf.DescriptorProto.ExtensionRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start', full_name='google.protobuf.DescriptorProto.ExtensionRange.start', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.DescriptorProto.ExtensionRange.end', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.DescriptorProto.ExtensionRange.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _DESCRIPTORPROTO_RESERVEDRANGE = _descriptor.Descriptor( + name='ReservedRange', + full_name='google.protobuf.DescriptorProto.ReservedRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start', full_name='google.protobuf.DescriptorProto.ReservedRange.start', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.DescriptorProto.ReservedRange.end', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _DESCRIPTORPROTO = _descriptor.Descriptor( + name='DescriptorProto', + full_name='google.protobuf.DescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.DescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='field', full_name='google.protobuf.DescriptorProto.field', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension', full_name='google.protobuf.DescriptorProto.extension', index=2, + number=6, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='nested_type', full_name='google.protobuf.DescriptorProto.nested_type', index=3, + number=3, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='enum_type', full_name='google.protobuf.DescriptorProto.enum_type', index=4, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extension_range', full_name='google.protobuf.DescriptorProto.extension_range', index=5, + number=5, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='oneof_decl', full_name='google.protobuf.DescriptorProto.oneof_decl', index=6, + number=8, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.DescriptorProto.options', index=7, + number=7, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_range', full_name='google.protobuf.DescriptorProto.reserved_range', index=8, + number=9, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_name', full_name='google.protobuf.DescriptorProto.reserved_name', index=9, + number=10, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_DESCRIPTORPROTO_EXTENSIONRANGE, _DESCRIPTORPROTO_RESERVEDRANGE, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _EXTENSIONRANGEOPTIONS = _descriptor.Descriptor( + name='ExtensionRangeOptions', + full_name='google.protobuf.ExtensionRangeOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.ExtensionRangeOptions.uninterpreted_option', index=0, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _FIELDDESCRIPTORPROTO = _descriptor.Descriptor( + name='FieldDescriptorProto', + full_name='google.protobuf.FieldDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.FieldDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='number', full_name='google.protobuf.FieldDescriptorProto.number', index=1, + number=3, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='label', full_name='google.protobuf.FieldDescriptorProto.label', index=2, + number=4, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', full_name='google.protobuf.FieldDescriptorProto.type', index=3, + number=5, type=14, cpp_type=8, label=1, + has_default_value=False, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type_name', full_name='google.protobuf.FieldDescriptorProto.type_name', index=4, + number=6, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='extendee', full_name='google.protobuf.FieldDescriptorProto.extendee', index=5, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='default_value', full_name='google.protobuf.FieldDescriptorProto.default_value', index=6, + number=7, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='oneof_index', full_name='google.protobuf.FieldDescriptorProto.oneof_index', index=7, + number=9, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='json_name', full_name='google.protobuf.FieldDescriptorProto.json_name', index=8, + number=10, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.FieldDescriptorProto.options', index=9, + number=8, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='proto3_optional', full_name='google.protobuf.FieldDescriptorProto.proto3_optional', index=10, + number=17, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _FIELDDESCRIPTORPROTO_TYPE, + _FIELDDESCRIPTORPROTO_LABEL, + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _ONEOFDESCRIPTORPROTO = _descriptor.Descriptor( + name='OneofDescriptorProto', + full_name='google.protobuf.OneofDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.OneofDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.OneofDescriptorProto.options', index=1, + number=2, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE = _descriptor.Descriptor( + name='EnumReservedRange', + full_name='google.protobuf.EnumDescriptorProto.EnumReservedRange', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start', full_name='google.protobuf.EnumDescriptorProto.EnumReservedRange.start', index=0, + number=1, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.EnumDescriptorProto.EnumReservedRange.end', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _ENUMDESCRIPTORPROTO = _descriptor.Descriptor( + name='EnumDescriptorProto', + full_name='google.protobuf.EnumDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.EnumDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', full_name='google.protobuf.EnumDescriptorProto.value', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.EnumDescriptorProto.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_range', full_name='google.protobuf.EnumDescriptorProto.reserved_range', index=3, + number=4, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reserved_name', full_name='google.protobuf.EnumDescriptorProto.reserved_name', index=4, + number=5, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _ENUMVALUEDESCRIPTORPROTO = _descriptor.Descriptor( + name='EnumValueDescriptorProto', + full_name='google.protobuf.EnumValueDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.EnumValueDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='number', full_name='google.protobuf.EnumValueDescriptorProto.number', index=1, + number=2, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.EnumValueDescriptorProto.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _SERVICEDESCRIPTORPROTO = _descriptor.Descriptor( + name='ServiceDescriptorProto', + full_name='google.protobuf.ServiceDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.ServiceDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='method', full_name='google.protobuf.ServiceDescriptorProto.method', index=1, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.ServiceDescriptorProto.options', index=2, + number=3, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _METHODDESCRIPTORPROTO = _descriptor.Descriptor( + name='MethodDescriptorProto', + full_name='google.protobuf.MethodDescriptorProto', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.MethodDescriptorProto.name', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='input_type', full_name='google.protobuf.MethodDescriptorProto.input_type', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='output_type', full_name='google.protobuf.MethodDescriptorProto.output_type', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', full_name='google.protobuf.MethodDescriptorProto.options', index=3, + number=4, type=11, cpp_type=10, label=1, + has_default_value=False, default_value=None, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_streaming', full_name='google.protobuf.MethodDescriptorProto.client_streaming', index=4, + number=5, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='server_streaming', full_name='google.protobuf.MethodDescriptorProto.server_streaming', index=5, + number=6, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _FILEOPTIONS = _descriptor.Descriptor( + name='FileOptions', + full_name='google.protobuf.FileOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='java_package', full_name='google.protobuf.FileOptions.java_package', index=0, + number=1, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_outer_classname', full_name='google.protobuf.FileOptions.java_outer_classname', index=1, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_multiple_files', full_name='google.protobuf.FileOptions.java_multiple_files', index=2, + number=10, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_generate_equals_and_hash', full_name='google.protobuf.FileOptions.java_generate_equals_and_hash', index=3, + number=20, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_string_check_utf8', full_name='google.protobuf.FileOptions.java_string_check_utf8', index=4, + number=27, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='optimize_for', full_name='google.protobuf.FileOptions.optimize_for', index=5, + number=9, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=1, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='go_package', full_name='google.protobuf.FileOptions.go_package', index=6, + number=11, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cc_generic_services', full_name='google.protobuf.FileOptions.cc_generic_services', index=7, + number=16, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='java_generic_services', full_name='google.protobuf.FileOptions.java_generic_services', index=8, + number=17, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='py_generic_services', full_name='google.protobuf.FileOptions.py_generic_services', index=9, + number=18, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_generic_services', full_name='google.protobuf.FileOptions.php_generic_services', index=10, + number=42, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.FileOptions.deprecated', index=11, + number=23, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cc_enable_arenas', full_name='google.protobuf.FileOptions.cc_enable_arenas', index=12, + number=31, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=True, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='objc_class_prefix', full_name='google.protobuf.FileOptions.objc_class_prefix', index=13, + number=36, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='csharp_namespace', full_name='google.protobuf.FileOptions.csharp_namespace', index=14, + number=37, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='swift_prefix', full_name='google.protobuf.FileOptions.swift_prefix', index=15, + number=39, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_class_prefix', full_name='google.protobuf.FileOptions.php_class_prefix', index=16, + number=40, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_namespace', full_name='google.protobuf.FileOptions.php_namespace', index=17, + number=41, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='php_metadata_namespace', full_name='google.protobuf.FileOptions.php_metadata_namespace', index=18, + number=44, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ruby_package', full_name='google.protobuf.FileOptions.ruby_package', index=19, + number=45, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.FileOptions.uninterpreted_option', index=20, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _FILEOPTIONS_OPTIMIZEMODE, + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _MESSAGEOPTIONS = _descriptor.Descriptor( + name='MessageOptions', + full_name='google.protobuf.MessageOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='message_set_wire_format', full_name='google.protobuf.MessageOptions.message_set_wire_format', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='no_standard_descriptor_accessor', full_name='google.protobuf.MessageOptions.no_standard_descriptor_accessor', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.MessageOptions.deprecated', index=2, + number=3, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='map_entry', full_name='google.protobuf.MessageOptions.map_entry', index=3, + number=7, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.MessageOptions.uninterpreted_option', index=4, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _FIELDOPTIONS = _descriptor.Descriptor( + name='FieldOptions', + full_name='google.protobuf.FieldOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='ctype', full_name='google.protobuf.FieldOptions.ctype', index=0, + number=1, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='packed', full_name='google.protobuf.FieldOptions.packed', index=1, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='jstype', full_name='google.protobuf.FieldOptions.jstype', index=2, + number=6, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lazy', full_name='google.protobuf.FieldOptions.lazy', index=3, + number=5, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='unverified_lazy', full_name='google.protobuf.FieldOptions.unverified_lazy', index=4, + number=15, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.FieldOptions.deprecated', index=5, + number=3, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='weak', full_name='google.protobuf.FieldOptions.weak', index=6, + number=10, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.FieldOptions.uninterpreted_option', index=7, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _FIELDOPTIONS_CTYPE, + _FIELDOPTIONS_JSTYPE, + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _ONEOFOPTIONS = _descriptor.Descriptor( + name='OneofOptions', + full_name='google.protobuf.OneofOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.OneofOptions.uninterpreted_option', index=0, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _ENUMOPTIONS = _descriptor.Descriptor( + name='EnumOptions', + full_name='google.protobuf.EnumOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='allow_alias', full_name='google.protobuf.EnumOptions.allow_alias', index=0, + number=2, type=8, cpp_type=7, label=1, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.EnumOptions.deprecated', index=1, + number=3, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.EnumOptions.uninterpreted_option', index=2, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _ENUMVALUEOPTIONS = _descriptor.Descriptor( + name='EnumValueOptions', + full_name='google.protobuf.EnumValueOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.EnumValueOptions.deprecated', index=0, + number=1, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.EnumValueOptions.uninterpreted_option', index=1, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _SERVICEOPTIONS = _descriptor.Descriptor( + name='ServiceOptions', + full_name='google.protobuf.ServiceOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.ServiceOptions.deprecated', index=0, + number=33, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.ServiceOptions.uninterpreted_option', index=1, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _METHODOPTIONS = _descriptor.Descriptor( + name='MethodOptions', + full_name='google.protobuf.MethodOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='deprecated', full_name='google.protobuf.MethodOptions.deprecated', index=0, + number=33, type=8, cpp_type=7, label=1, + has_default_value=True, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='idempotency_level', full_name='google.protobuf.MethodOptions.idempotency_level', index=1, + number=34, type=14, cpp_type=8, label=1, + has_default_value=True, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uninterpreted_option', full_name='google.protobuf.MethodOptions.uninterpreted_option', index=2, + number=999, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + _METHODOPTIONS_IDEMPOTENCYLEVEL, + ], + serialized_options=None, + is_extendable=True, + syntax='proto2', + extension_ranges=[(1000, 536870912), ], + oneofs=[ + ], + ) + + + _UNINTERPRETEDOPTION_NAMEPART = _descriptor.Descriptor( + name='NamePart', + full_name='google.protobuf.UninterpretedOption.NamePart', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name_part', full_name='google.protobuf.UninterpretedOption.NamePart.name_part', index=0, + number=1, type=9, cpp_type=9, label=2, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_extension', full_name='google.protobuf.UninterpretedOption.NamePart.is_extension', index=1, + number=2, type=8, cpp_type=7, label=2, + has_default_value=False, default_value=False, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _UNINTERPRETEDOPTION = _descriptor.Descriptor( + name='UninterpretedOption', + full_name='google.protobuf.UninterpretedOption', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='name', full_name='google.protobuf.UninterpretedOption.name', index=0, + number=2, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='identifier_value', full_name='google.protobuf.UninterpretedOption.identifier_value', index=1, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='positive_int_value', full_name='google.protobuf.UninterpretedOption.positive_int_value', index=2, + number=4, type=4, cpp_type=4, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='negative_int_value', full_name='google.protobuf.UninterpretedOption.negative_int_value', index=3, + number=5, type=3, cpp_type=2, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='double_value', full_name='google.protobuf.UninterpretedOption.double_value', index=4, + number=6, type=1, cpp_type=5, label=1, + has_default_value=False, default_value=float(0), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='string_value', full_name='google.protobuf.UninterpretedOption.string_value', index=5, + number=7, type=12, cpp_type=9, label=1, + has_default_value=False, default_value=b"", + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='aggregate_value', full_name='google.protobuf.UninterpretedOption.aggregate_value', index=6, + number=8, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_UNINTERPRETEDOPTION_NAMEPART, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _SOURCECODEINFO_LOCATION = _descriptor.Descriptor( + name='Location', + full_name='google.protobuf.SourceCodeInfo.Location', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='path', full_name='google.protobuf.SourceCodeInfo.Location.path', index=0, + number=1, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='span', full_name='google.protobuf.SourceCodeInfo.Location.span', index=1, + number=2, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='leading_comments', full_name='google.protobuf.SourceCodeInfo.Location.leading_comments', index=2, + number=3, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='trailing_comments', full_name='google.protobuf.SourceCodeInfo.Location.trailing_comments', index=3, + number=4, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='leading_detached_comments', full_name='google.protobuf.SourceCodeInfo.Location.leading_detached_comments', index=4, + number=6, type=9, cpp_type=9, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _SOURCECODEINFO = _descriptor.Descriptor( + name='SourceCodeInfo', + full_name='google.protobuf.SourceCodeInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='location', full_name='google.protobuf.SourceCodeInfo.location', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_SOURCECODEINFO_LOCATION, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + + _GENERATEDCODEINFO_ANNOTATION = _descriptor.Descriptor( + name='Annotation', + full_name='google.protobuf.GeneratedCodeInfo.Annotation', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='path', full_name='google.protobuf.GeneratedCodeInfo.Annotation.path', index=0, + number=1, type=5, cpp_type=1, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='source_file', full_name='google.protobuf.GeneratedCodeInfo.Annotation.source_file', index=1, + number=2, type=9, cpp_type=9, label=1, + has_default_value=False, default_value=b"".decode('utf-8'), + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='begin', full_name='google.protobuf.GeneratedCodeInfo.Annotation.begin', index=2, + number=3, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', full_name='google.protobuf.GeneratedCodeInfo.Annotation.end', index=3, + number=4, type=5, cpp_type=1, label=1, + has_default_value=False, default_value=0, + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _GENERATEDCODEINFO = _descriptor.Descriptor( + name='GeneratedCodeInfo', + full_name='google.protobuf.GeneratedCodeInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='annotation', full_name='google.protobuf.GeneratedCodeInfo.annotation', index=0, + number=1, type=11, cpp_type=10, label=3, + has_default_value=False, default_value=[], + message_type=None, enum_type=None, containing_type=None, + is_extension=False, extension_scope=None, + serialized_options=None, file=DESCRIPTOR, create_key=_descriptor._internal_create_key), + ], + extensions=[ + ], + nested_types=[_GENERATEDCODEINFO_ANNOTATION, ], + enum_types=[ + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + ], + ) + + _FILEDESCRIPTORSET.fields_by_name['file'].message_type = _FILEDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['message_type'].message_type = _DESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['enum_type'].message_type = _ENUMDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['service'].message_type = _SERVICEDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['extension'].message_type = _FIELDDESCRIPTORPROTO + _FILEDESCRIPTORPROTO.fields_by_name['options'].message_type = _FILEOPTIONS + _FILEDESCRIPTORPROTO.fields_by_name['source_code_info'].message_type = _SOURCECODEINFO + _DESCRIPTORPROTO_EXTENSIONRANGE.fields_by_name['options'].message_type = _EXTENSIONRANGEOPTIONS + _DESCRIPTORPROTO_EXTENSIONRANGE.containing_type = _DESCRIPTORPROTO + _DESCRIPTORPROTO_RESERVEDRANGE.containing_type = _DESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['field'].message_type = _FIELDDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['extension'].message_type = _FIELDDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['nested_type'].message_type = _DESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['enum_type'].message_type = _ENUMDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['extension_range'].message_type = _DESCRIPTORPROTO_EXTENSIONRANGE + _DESCRIPTORPROTO.fields_by_name['oneof_decl'].message_type = _ONEOFDESCRIPTORPROTO + _DESCRIPTORPROTO.fields_by_name['options'].message_type = _MESSAGEOPTIONS + _DESCRIPTORPROTO.fields_by_name['reserved_range'].message_type = _DESCRIPTORPROTO_RESERVEDRANGE + _EXTENSIONRANGEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FIELDDESCRIPTORPROTO.fields_by_name['label'].enum_type = _FIELDDESCRIPTORPROTO_LABEL + _FIELDDESCRIPTORPROTO.fields_by_name['type'].enum_type = _FIELDDESCRIPTORPROTO_TYPE + _FIELDDESCRIPTORPROTO.fields_by_name['options'].message_type = _FIELDOPTIONS + _FIELDDESCRIPTORPROTO_TYPE.containing_type = _FIELDDESCRIPTORPROTO + _FIELDDESCRIPTORPROTO_LABEL.containing_type = _FIELDDESCRIPTORPROTO + _ONEOFDESCRIPTORPROTO.fields_by_name['options'].message_type = _ONEOFOPTIONS + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE.containing_type = _ENUMDESCRIPTORPROTO + _ENUMDESCRIPTORPROTO.fields_by_name['value'].message_type = _ENUMVALUEDESCRIPTORPROTO + _ENUMDESCRIPTORPROTO.fields_by_name['options'].message_type = _ENUMOPTIONS + _ENUMDESCRIPTORPROTO.fields_by_name['reserved_range'].message_type = _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE + _ENUMVALUEDESCRIPTORPROTO.fields_by_name['options'].message_type = _ENUMVALUEOPTIONS + _SERVICEDESCRIPTORPROTO.fields_by_name['method'].message_type = _METHODDESCRIPTORPROTO + _SERVICEDESCRIPTORPROTO.fields_by_name['options'].message_type = _SERVICEOPTIONS + _METHODDESCRIPTORPROTO.fields_by_name['options'].message_type = _METHODOPTIONS + _FILEOPTIONS.fields_by_name['optimize_for'].enum_type = _FILEOPTIONS_OPTIMIZEMODE + _FILEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FILEOPTIONS_OPTIMIZEMODE.containing_type = _FILEOPTIONS + _MESSAGEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FIELDOPTIONS.fields_by_name['ctype'].enum_type = _FIELDOPTIONS_CTYPE + _FIELDOPTIONS.fields_by_name['jstype'].enum_type = _FIELDOPTIONS_JSTYPE + _FIELDOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _FIELDOPTIONS_CTYPE.containing_type = _FIELDOPTIONS + _FIELDOPTIONS_JSTYPE.containing_type = _FIELDOPTIONS + _ONEOFOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _ENUMOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _ENUMVALUEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _SERVICEOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _METHODOPTIONS.fields_by_name['idempotency_level'].enum_type = _METHODOPTIONS_IDEMPOTENCYLEVEL + _METHODOPTIONS.fields_by_name['uninterpreted_option'].message_type = _UNINTERPRETEDOPTION + _METHODOPTIONS_IDEMPOTENCYLEVEL.containing_type = _METHODOPTIONS + _UNINTERPRETEDOPTION_NAMEPART.containing_type = _UNINTERPRETEDOPTION + _UNINTERPRETEDOPTION.fields_by_name['name'].message_type = _UNINTERPRETEDOPTION_NAMEPART + _SOURCECODEINFO_LOCATION.containing_type = _SOURCECODEINFO + _SOURCECODEINFO.fields_by_name['location'].message_type = _SOURCECODEINFO_LOCATION + _GENERATEDCODEINFO_ANNOTATION.containing_type = _GENERATEDCODEINFO + _GENERATEDCODEINFO.fields_by_name['annotation'].message_type = _GENERATEDCODEINFO_ANNOTATION + DESCRIPTOR.message_types_by_name['FileDescriptorSet'] = _FILEDESCRIPTORSET + DESCRIPTOR.message_types_by_name['FileDescriptorProto'] = _FILEDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['DescriptorProto'] = _DESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['ExtensionRangeOptions'] = _EXTENSIONRANGEOPTIONS + DESCRIPTOR.message_types_by_name['FieldDescriptorProto'] = _FIELDDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['OneofDescriptorProto'] = _ONEOFDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['EnumDescriptorProto'] = _ENUMDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['EnumValueDescriptorProto'] = _ENUMVALUEDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['ServiceDescriptorProto'] = _SERVICEDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['MethodDescriptorProto'] = _METHODDESCRIPTORPROTO + DESCRIPTOR.message_types_by_name['FileOptions'] = _FILEOPTIONS + DESCRIPTOR.message_types_by_name['MessageOptions'] = _MESSAGEOPTIONS + DESCRIPTOR.message_types_by_name['FieldOptions'] = _FIELDOPTIONS + DESCRIPTOR.message_types_by_name['OneofOptions'] = _ONEOFOPTIONS + DESCRIPTOR.message_types_by_name['EnumOptions'] = _ENUMOPTIONS + DESCRIPTOR.message_types_by_name['EnumValueOptions'] = _ENUMVALUEOPTIONS + DESCRIPTOR.message_types_by_name['ServiceOptions'] = _SERVICEOPTIONS + DESCRIPTOR.message_types_by_name['MethodOptions'] = _METHODOPTIONS + DESCRIPTOR.message_types_by_name['UninterpretedOption'] = _UNINTERPRETEDOPTION + DESCRIPTOR.message_types_by_name['SourceCodeInfo'] = _SOURCECODEINFO + DESCRIPTOR.message_types_by_name['GeneratedCodeInfo'] = _GENERATEDCODEINFO + _sym_db.RegisterFileDescriptor(DESCRIPTOR) + +else: + _builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.descriptor_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + _FILEDESCRIPTORSET._serialized_start=53 + _FILEDESCRIPTORSET._serialized_end=124 + _FILEDESCRIPTORPROTO._serialized_start=127 + _FILEDESCRIPTORPROTO._serialized_end=602 + _DESCRIPTORPROTO._serialized_start=605 + _DESCRIPTORPROTO._serialized_end=1286 + _DESCRIPTORPROTO_EXTENSIONRANGE._serialized_start=1140 + _DESCRIPTORPROTO_EXTENSIONRANGE._serialized_end=1241 + _DESCRIPTORPROTO_RESERVEDRANGE._serialized_start=1243 + _DESCRIPTORPROTO_RESERVEDRANGE._serialized_end=1286 + _EXTENSIONRANGEOPTIONS._serialized_start=1288 + _EXTENSIONRANGEOPTIONS._serialized_end=1391 + _FIELDDESCRIPTORPROTO._serialized_start=1394 + _FIELDDESCRIPTORPROTO._serialized_end=2119 + _FIELDDESCRIPTORPROTO_TYPE._serialized_start=1740 + _FIELDDESCRIPTORPROTO_TYPE._serialized_end=2050 + _FIELDDESCRIPTORPROTO_LABEL._serialized_start=2052 + _FIELDDESCRIPTORPROTO_LABEL._serialized_end=2119 + _ONEOFDESCRIPTORPROTO._serialized_start=2121 + _ONEOFDESCRIPTORPROTO._serialized_end=2205 + _ENUMDESCRIPTORPROTO._serialized_start=2208 + _ENUMDESCRIPTORPROTO._serialized_end=2500 + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE._serialized_start=2453 + _ENUMDESCRIPTORPROTO_ENUMRESERVEDRANGE._serialized_end=2500 + _ENUMVALUEDESCRIPTORPROTO._serialized_start=2502 + _ENUMVALUEDESCRIPTORPROTO._serialized_end=2610 + _SERVICEDESCRIPTORPROTO._serialized_start=2613 + _SERVICEDESCRIPTORPROTO._serialized_end=2757 + _METHODDESCRIPTORPROTO._serialized_start=2760 + _METHODDESCRIPTORPROTO._serialized_end=2953 + _FILEOPTIONS._serialized_start=2956 + _FILEOPTIONS._serialized_end=3761 + _FILEOPTIONS_OPTIMIZEMODE._serialized_start=3686 + _FILEOPTIONS_OPTIMIZEMODE._serialized_end=3744 + _MESSAGEOPTIONS._serialized_start=3764 + _MESSAGEOPTIONS._serialized_end=4024 + _FIELDOPTIONS._serialized_start=4027 + _FIELDOPTIONS._serialized_end=4473 + _FIELDOPTIONS_CTYPE._serialized_start=4354 + _FIELDOPTIONS_CTYPE._serialized_end=4401 + _FIELDOPTIONS_JSTYPE._serialized_start=4403 + _FIELDOPTIONS_JSTYPE._serialized_end=4456 + _ONEOFOPTIONS._serialized_start=4475 + _ONEOFOPTIONS._serialized_end=4569 + _ENUMOPTIONS._serialized_start=4572 + _ENUMOPTIONS._serialized_end=4719 + _ENUMVALUEOPTIONS._serialized_start=4721 + _ENUMVALUEOPTIONS._serialized_end=4846 + _SERVICEOPTIONS._serialized_start=4848 + _SERVICEOPTIONS._serialized_end=4971 + _METHODOPTIONS._serialized_start=4974 + _METHODOPTIONS._serialized_end=5275 + _METHODOPTIONS_IDEMPOTENCYLEVEL._serialized_start=5184 + _METHODOPTIONS_IDEMPOTENCYLEVEL._serialized_end=5264 + _UNINTERPRETEDOPTION._serialized_start=5278 + _UNINTERPRETEDOPTION._serialized_end=5564 + _UNINTERPRETEDOPTION_NAMEPART._serialized_start=5513 + _UNINTERPRETEDOPTION_NAMEPART._serialized_end=5564 + _SOURCECODEINFO._serialized_start=5567 + _SOURCECODEINFO._serialized_end=5780 + _SOURCECODEINFO_LOCATION._serialized_start=5646 + _SOURCECODEINFO_LOCATION._serialized_end=5780 + _GENERATEDCODEINFO._serialized_start=5783 + _GENERATEDCODEINFO._serialized_end=5950 + _GENERATEDCODEINFO_ANNOTATION._serialized_start=5871 + _GENERATEDCODEINFO_ANNOTATION._serialized_end=5950 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/descriptor_pool.py b/lib/protobuf/descriptor_pool.py new file mode 100644 index 0000000..911372a --- /dev/null +++ b/lib/protobuf/descriptor_pool.py @@ -0,0 +1,1295 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Provides DescriptorPool to use as a container for proto2 descriptors. + +The DescriptorPool is used in conjection with a DescriptorDatabase to maintain +a collection of protocol buffer descriptors for use when dynamically creating +message types at runtime. + +For most applications protocol buffers should be used via modules generated by +the protocol buffer compiler tool. This should only be used when the type of +protocol buffers used in an application or library cannot be predetermined. + +Below is a straightforward example on how to use this class:: + + pool = DescriptorPool() + file_descriptor_protos = [ ... ] + for file_descriptor_proto in file_descriptor_protos: + pool.Add(file_descriptor_proto) + my_message_descriptor = pool.FindMessageTypeByName('some.package.MessageType') + +The message descriptor can be used in conjunction with the message_factory +module in order to create a protocol buffer class that can be encoded and +decoded. + +If you want to get a Python class for the specified proto, use the +helper functions inside google.protobuf.message_factory +directly instead of this class. +""" + +__author__ = 'matthewtoia@google.com (Matt Toia)' + +import collections +import warnings + +from google.protobuf import descriptor +from google.protobuf import descriptor_database +from google.protobuf import text_encoding + + +_USE_C_DESCRIPTORS = descriptor._USE_C_DESCRIPTORS # pylint: disable=protected-access + + +def _Deprecated(func): + """Mark functions as deprecated.""" + + def NewFunc(*args, **kwargs): + warnings.warn( + 'Call to deprecated function %s(). Note: Do add unlinked descriptors ' + 'to descriptor_pool is wrong. Use Add() or AddSerializedFile() ' + 'instead.' % func.__name__, + category=DeprecationWarning) + return func(*args, **kwargs) + NewFunc.__name__ = func.__name__ + NewFunc.__doc__ = func.__doc__ + NewFunc.__dict__.update(func.__dict__) + return NewFunc + + +def _NormalizeFullyQualifiedName(name): + """Remove leading period from fully-qualified type name. + + Due to b/13860351 in descriptor_database.py, types in the root namespace are + generated with a leading period. This function removes that prefix. + + Args: + name (str): The fully-qualified symbol name. + + Returns: + str: The normalized fully-qualified symbol name. + """ + return name.lstrip('.') + + +def _OptionsOrNone(descriptor_proto): + """Returns the value of the field `options`, or None if it is not set.""" + if descriptor_proto.HasField('options'): + return descriptor_proto.options + else: + return None + + +def _IsMessageSetExtension(field): + return (field.is_extension and + field.containing_type.has_options and + field.containing_type.GetOptions().message_set_wire_format and + field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL) + + +class DescriptorPool(object): + """A collection of protobufs dynamically constructed by descriptor protos.""" + + if _USE_C_DESCRIPTORS: + + def __new__(cls, descriptor_db=None): + # pylint: disable=protected-access + return descriptor._message.DescriptorPool(descriptor_db) + + def __init__(self, descriptor_db=None): + """Initializes a Pool of proto buffs. + + The descriptor_db argument to the constructor is provided to allow + specialized file descriptor proto lookup code to be triggered on demand. An + example would be an implementation which will read and compile a file + specified in a call to FindFileByName() and not require the call to Add() + at all. Results from this database will be cached internally here as well. + + Args: + descriptor_db: A secondary source of file descriptors. + """ + + self._internal_db = descriptor_database.DescriptorDatabase() + self._descriptor_db = descriptor_db + self._descriptors = {} + self._enum_descriptors = {} + self._service_descriptors = {} + self._file_descriptors = {} + self._toplevel_extensions = {} + # TODO(jieluo): Remove _file_desc_by_toplevel_extension after + # maybe year 2020 for compatibility issue (with 3.4.1 only). + self._file_desc_by_toplevel_extension = {} + self._top_enum_values = {} + # We store extensions in two two-level mappings: The first key is the + # descriptor of the message being extended, the second key is the extension + # full name or its tag number. + self._extensions_by_name = collections.defaultdict(dict) + self._extensions_by_number = collections.defaultdict(dict) + + def _CheckConflictRegister(self, desc, desc_name, file_name): + """Check if the descriptor name conflicts with another of the same name. + + Args: + desc: Descriptor of a message, enum, service, extension or enum value. + desc_name (str): the full name of desc. + file_name (str): The file name of descriptor. + """ + for register, descriptor_type in [ + (self._descriptors, descriptor.Descriptor), + (self._enum_descriptors, descriptor.EnumDescriptor), + (self._service_descriptors, descriptor.ServiceDescriptor), + (self._toplevel_extensions, descriptor.FieldDescriptor), + (self._top_enum_values, descriptor.EnumValueDescriptor)]: + if desc_name in register: + old_desc = register[desc_name] + if isinstance(old_desc, descriptor.EnumValueDescriptor): + old_file = old_desc.type.file.name + else: + old_file = old_desc.file.name + + if not isinstance(desc, descriptor_type) or ( + old_file != file_name): + error_msg = ('Conflict register for file "' + file_name + + '": ' + desc_name + + ' is already defined in file "' + + old_file + '". Please fix the conflict by adding ' + 'package name on the proto file, or use different ' + 'name for the duplication.') + if isinstance(desc, descriptor.EnumValueDescriptor): + error_msg += ('\nNote: enum values appear as ' + 'siblings of the enum type instead of ' + 'children of it.') + + raise TypeError(error_msg) + + return + + def Add(self, file_desc_proto): + """Adds the FileDescriptorProto and its types to this pool. + + Args: + file_desc_proto (FileDescriptorProto): The file descriptor to add. + """ + + self._internal_db.Add(file_desc_proto) + + def AddSerializedFile(self, serialized_file_desc_proto): + """Adds the FileDescriptorProto and its types to this pool. + + Args: + serialized_file_desc_proto (bytes): A bytes string, serialization of the + :class:`FileDescriptorProto` to add. + + Returns: + FileDescriptor: Descriptor for the added file. + """ + + # pylint: disable=g-import-not-at-top + from google.protobuf import descriptor_pb2 + file_desc_proto = descriptor_pb2.FileDescriptorProto.FromString( + serialized_file_desc_proto) + file_desc = self._ConvertFileProtoToFileDescriptor(file_desc_proto) + file_desc.serialized_pb = serialized_file_desc_proto + return file_desc + + # Add Descriptor to descriptor pool is dreprecated. Please use Add() + # or AddSerializedFile() to add a FileDescriptorProto instead. + @_Deprecated + def AddDescriptor(self, desc): + self._AddDescriptor(desc) + + # Never call this method. It is for internal usage only. + def _AddDescriptor(self, desc): + """Adds a Descriptor to the pool, non-recursively. + + If the Descriptor contains nested messages or enums, the caller must + explicitly register them. This method also registers the FileDescriptor + associated with the message. + + Args: + desc: A Descriptor. + """ + if not isinstance(desc, descriptor.Descriptor): + raise TypeError('Expected instance of descriptor.Descriptor.') + + self._CheckConflictRegister(desc, desc.full_name, desc.file.name) + + self._descriptors[desc.full_name] = desc + self._AddFileDescriptor(desc.file) + + # Add EnumDescriptor to descriptor pool is dreprecated. Please use Add() + # or AddSerializedFile() to add a FileDescriptorProto instead. + @_Deprecated + def AddEnumDescriptor(self, enum_desc): + self._AddEnumDescriptor(enum_desc) + + # Never call this method. It is for internal usage only. + def _AddEnumDescriptor(self, enum_desc): + """Adds an EnumDescriptor to the pool. + + This method also registers the FileDescriptor associated with the enum. + + Args: + enum_desc: An EnumDescriptor. + """ + + if not isinstance(enum_desc, descriptor.EnumDescriptor): + raise TypeError('Expected instance of descriptor.EnumDescriptor.') + + file_name = enum_desc.file.name + self._CheckConflictRegister(enum_desc, enum_desc.full_name, file_name) + self._enum_descriptors[enum_desc.full_name] = enum_desc + + # Top enum values need to be indexed. + # Count the number of dots to see whether the enum is toplevel or nested + # in a message. We cannot use enum_desc.containing_type at this stage. + if enum_desc.file.package: + top_level = (enum_desc.full_name.count('.') + - enum_desc.file.package.count('.') == 1) + else: + top_level = enum_desc.full_name.count('.') == 0 + if top_level: + file_name = enum_desc.file.name + package = enum_desc.file.package + for enum_value in enum_desc.values: + full_name = _NormalizeFullyQualifiedName( + '.'.join((package, enum_value.name))) + self._CheckConflictRegister(enum_value, full_name, file_name) + self._top_enum_values[full_name] = enum_value + self._AddFileDescriptor(enum_desc.file) + + # Add ServiceDescriptor to descriptor pool is dreprecated. Please use Add() + # or AddSerializedFile() to add a FileDescriptorProto instead. + @_Deprecated + def AddServiceDescriptor(self, service_desc): + self._AddServiceDescriptor(service_desc) + + # Never call this method. It is for internal usage only. + def _AddServiceDescriptor(self, service_desc): + """Adds a ServiceDescriptor to the pool. + + Args: + service_desc: A ServiceDescriptor. + """ + + if not isinstance(service_desc, descriptor.ServiceDescriptor): + raise TypeError('Expected instance of descriptor.ServiceDescriptor.') + + self._CheckConflictRegister(service_desc, service_desc.full_name, + service_desc.file.name) + self._service_descriptors[service_desc.full_name] = service_desc + + # Add ExtensionDescriptor to descriptor pool is dreprecated. Please use Add() + # or AddSerializedFile() to add a FileDescriptorProto instead. + @_Deprecated + def AddExtensionDescriptor(self, extension): + self._AddExtensionDescriptor(extension) + + # Never call this method. It is for internal usage only. + def _AddExtensionDescriptor(self, extension): + """Adds a FieldDescriptor describing an extension to the pool. + + Args: + extension: A FieldDescriptor. + + Raises: + AssertionError: when another extension with the same number extends the + same message. + TypeError: when the specified extension is not a + descriptor.FieldDescriptor. + """ + if not (isinstance(extension, descriptor.FieldDescriptor) and + extension.is_extension): + raise TypeError('Expected an extension descriptor.') + + if extension.extension_scope is None: + self._toplevel_extensions[extension.full_name] = extension + + try: + existing_desc = self._extensions_by_number[ + extension.containing_type][extension.number] + except KeyError: + pass + else: + if extension is not existing_desc: + raise AssertionError( + 'Extensions "%s" and "%s" both try to extend message type "%s" ' + 'with field number %d.' % + (extension.full_name, existing_desc.full_name, + extension.containing_type.full_name, extension.number)) + + self._extensions_by_number[extension.containing_type][ + extension.number] = extension + self._extensions_by_name[extension.containing_type][ + extension.full_name] = extension + + # Also register MessageSet extensions with the type name. + if _IsMessageSetExtension(extension): + self._extensions_by_name[extension.containing_type][ + extension.message_type.full_name] = extension + + @_Deprecated + def AddFileDescriptor(self, file_desc): + self._InternalAddFileDescriptor(file_desc) + + # Never call this method. It is for internal usage only. + def _InternalAddFileDescriptor(self, file_desc): + """Adds a FileDescriptor to the pool, non-recursively. + + If the FileDescriptor contains messages or enums, the caller must explicitly + register them. + + Args: + file_desc: A FileDescriptor. + """ + + self._AddFileDescriptor(file_desc) + # TODO(jieluo): This is a temporary solution for FieldDescriptor.file. + # FieldDescriptor.file is added in code gen. Remove this solution after + # maybe 2020 for compatibility reason (with 3.4.1 only). + for extension in file_desc.extensions_by_name.values(): + self._file_desc_by_toplevel_extension[ + extension.full_name] = file_desc + + def _AddFileDescriptor(self, file_desc): + """Adds a FileDescriptor to the pool, non-recursively. + + If the FileDescriptor contains messages or enums, the caller must explicitly + register them. + + Args: + file_desc: A FileDescriptor. + """ + + if not isinstance(file_desc, descriptor.FileDescriptor): + raise TypeError('Expected instance of descriptor.FileDescriptor.') + self._file_descriptors[file_desc.name] = file_desc + + def FindFileByName(self, file_name): + """Gets a FileDescriptor by file name. + + Args: + file_name (str): The path to the file to get a descriptor for. + + Returns: + FileDescriptor: The descriptor for the named file. + + Raises: + KeyError: if the file cannot be found in the pool. + """ + + try: + return self._file_descriptors[file_name] + except KeyError: + pass + + try: + file_proto = self._internal_db.FindFileByName(file_name) + except KeyError as error: + if self._descriptor_db: + file_proto = self._descriptor_db.FindFileByName(file_name) + else: + raise error + if not file_proto: + raise KeyError('Cannot find a file named %s' % file_name) + return self._ConvertFileProtoToFileDescriptor(file_proto) + + def FindFileContainingSymbol(self, symbol): + """Gets the FileDescriptor for the file containing the specified symbol. + + Args: + symbol (str): The name of the symbol to search for. + + Returns: + FileDescriptor: Descriptor for the file that contains the specified + symbol. + + Raises: + KeyError: if the file cannot be found in the pool. + """ + + symbol = _NormalizeFullyQualifiedName(symbol) + try: + return self._InternalFindFileContainingSymbol(symbol) + except KeyError: + pass + + try: + # Try fallback database. Build and find again if possible. + self._FindFileContainingSymbolInDb(symbol) + return self._InternalFindFileContainingSymbol(symbol) + except KeyError: + raise KeyError('Cannot find a file containing %s' % symbol) + + def _InternalFindFileContainingSymbol(self, symbol): + """Gets the already built FileDescriptor containing the specified symbol. + + Args: + symbol (str): The name of the symbol to search for. + + Returns: + FileDescriptor: Descriptor for the file that contains the specified + symbol. + + Raises: + KeyError: if the file cannot be found in the pool. + """ + try: + return self._descriptors[symbol].file + except KeyError: + pass + + try: + return self._enum_descriptors[symbol].file + except KeyError: + pass + + try: + return self._service_descriptors[symbol].file + except KeyError: + pass + + try: + return self._top_enum_values[symbol].type.file + except KeyError: + pass + + try: + return self._file_desc_by_toplevel_extension[symbol] + except KeyError: + pass + + # Try fields, enum values and nested extensions inside a message. + top_name, _, sub_name = symbol.rpartition('.') + try: + message = self.FindMessageTypeByName(top_name) + assert (sub_name in message.extensions_by_name or + sub_name in message.fields_by_name or + sub_name in message.enum_values_by_name) + return message.file + except (KeyError, AssertionError): + raise KeyError('Cannot find a file containing %s' % symbol) + + def FindMessageTypeByName(self, full_name): + """Loads the named descriptor from the pool. + + Args: + full_name (str): The full name of the descriptor to load. + + Returns: + Descriptor: The descriptor for the named type. + + Raises: + KeyError: if the message cannot be found in the pool. + """ + + full_name = _NormalizeFullyQualifiedName(full_name) + if full_name not in self._descriptors: + self._FindFileContainingSymbolInDb(full_name) + return self._descriptors[full_name] + + def FindEnumTypeByName(self, full_name): + """Loads the named enum descriptor from the pool. + + Args: + full_name (str): The full name of the enum descriptor to load. + + Returns: + EnumDescriptor: The enum descriptor for the named type. + + Raises: + KeyError: if the enum cannot be found in the pool. + """ + + full_name = _NormalizeFullyQualifiedName(full_name) + if full_name not in self._enum_descriptors: + self._FindFileContainingSymbolInDb(full_name) + return self._enum_descriptors[full_name] + + def FindFieldByName(self, full_name): + """Loads the named field descriptor from the pool. + + Args: + full_name (str): The full name of the field descriptor to load. + + Returns: + FieldDescriptor: The field descriptor for the named field. + + Raises: + KeyError: if the field cannot be found in the pool. + """ + full_name = _NormalizeFullyQualifiedName(full_name) + message_name, _, field_name = full_name.rpartition('.') + message_descriptor = self.FindMessageTypeByName(message_name) + return message_descriptor.fields_by_name[field_name] + + def FindOneofByName(self, full_name): + """Loads the named oneof descriptor from the pool. + + Args: + full_name (str): The full name of the oneof descriptor to load. + + Returns: + OneofDescriptor: The oneof descriptor for the named oneof. + + Raises: + KeyError: if the oneof cannot be found in the pool. + """ + full_name = _NormalizeFullyQualifiedName(full_name) + message_name, _, oneof_name = full_name.rpartition('.') + message_descriptor = self.FindMessageTypeByName(message_name) + return message_descriptor.oneofs_by_name[oneof_name] + + def FindExtensionByName(self, full_name): + """Loads the named extension descriptor from the pool. + + Args: + full_name (str): The full name of the extension descriptor to load. + + Returns: + FieldDescriptor: The field descriptor for the named extension. + + Raises: + KeyError: if the extension cannot be found in the pool. + """ + full_name = _NormalizeFullyQualifiedName(full_name) + try: + # The proto compiler does not give any link between the FileDescriptor + # and top-level extensions unless the FileDescriptorProto is added to + # the DescriptorDatabase, but this can impact memory usage. + # So we registered these extensions by name explicitly. + return self._toplevel_extensions[full_name] + except KeyError: + pass + message_name, _, extension_name = full_name.rpartition('.') + try: + # Most extensions are nested inside a message. + scope = self.FindMessageTypeByName(message_name) + except KeyError: + # Some extensions are defined at file scope. + scope = self._FindFileContainingSymbolInDb(full_name) + return scope.extensions_by_name[extension_name] + + def FindExtensionByNumber(self, message_descriptor, number): + """Gets the extension of the specified message with the specified number. + + Extensions have to be registered to this pool by calling :func:`Add` or + :func:`AddExtensionDescriptor`. + + Args: + message_descriptor (Descriptor): descriptor of the extended message. + number (int): Number of the extension field. + + Returns: + FieldDescriptor: The descriptor for the extension. + + Raises: + KeyError: when no extension with the given number is known for the + specified message. + """ + try: + return self._extensions_by_number[message_descriptor][number] + except KeyError: + self._TryLoadExtensionFromDB(message_descriptor, number) + return self._extensions_by_number[message_descriptor][number] + + def FindAllExtensions(self, message_descriptor): + """Gets all the known extensions of a given message. + + Extensions have to be registered to this pool by build related + :func:`Add` or :func:`AddExtensionDescriptor`. + + Args: + message_descriptor (Descriptor): Descriptor of the extended message. + + Returns: + list[FieldDescriptor]: Field descriptors describing the extensions. + """ + # Fallback to descriptor db if FindAllExtensionNumbers is provided. + if self._descriptor_db and hasattr( + self._descriptor_db, 'FindAllExtensionNumbers'): + full_name = message_descriptor.full_name + all_numbers = self._descriptor_db.FindAllExtensionNumbers(full_name) + for number in all_numbers: + if number in self._extensions_by_number[message_descriptor]: + continue + self._TryLoadExtensionFromDB(message_descriptor, number) + + return list(self._extensions_by_number[message_descriptor].values()) + + def _TryLoadExtensionFromDB(self, message_descriptor, number): + """Try to Load extensions from descriptor db. + + Args: + message_descriptor: descriptor of the extended message. + number: the extension number that needs to be loaded. + """ + if not self._descriptor_db: + return + # Only supported when FindFileContainingExtension is provided. + if not hasattr( + self._descriptor_db, 'FindFileContainingExtension'): + return + + full_name = message_descriptor.full_name + file_proto = self._descriptor_db.FindFileContainingExtension( + full_name, number) + + if file_proto is None: + return + + try: + self._ConvertFileProtoToFileDescriptor(file_proto) + except: + warn_msg = ('Unable to load proto file %s for extension number %d.' % + (file_proto.name, number)) + warnings.warn(warn_msg, RuntimeWarning) + + def FindServiceByName(self, full_name): + """Loads the named service descriptor from the pool. + + Args: + full_name (str): The full name of the service descriptor to load. + + Returns: + ServiceDescriptor: The service descriptor for the named service. + + Raises: + KeyError: if the service cannot be found in the pool. + """ + full_name = _NormalizeFullyQualifiedName(full_name) + if full_name not in self._service_descriptors: + self._FindFileContainingSymbolInDb(full_name) + return self._service_descriptors[full_name] + + def FindMethodByName(self, full_name): + """Loads the named service method descriptor from the pool. + + Args: + full_name (str): The full name of the method descriptor to load. + + Returns: + MethodDescriptor: The method descriptor for the service method. + + Raises: + KeyError: if the method cannot be found in the pool. + """ + full_name = _NormalizeFullyQualifiedName(full_name) + service_name, _, method_name = full_name.rpartition('.') + service_descriptor = self.FindServiceByName(service_name) + return service_descriptor.methods_by_name[method_name] + + def _FindFileContainingSymbolInDb(self, symbol): + """Finds the file in descriptor DB containing the specified symbol. + + Args: + symbol (str): The name of the symbol to search for. + + Returns: + FileDescriptor: The file that contains the specified symbol. + + Raises: + KeyError: if the file cannot be found in the descriptor database. + """ + try: + file_proto = self._internal_db.FindFileContainingSymbol(symbol) + except KeyError as error: + if self._descriptor_db: + file_proto = self._descriptor_db.FindFileContainingSymbol(symbol) + else: + raise error + if not file_proto: + raise KeyError('Cannot find a file containing %s' % symbol) + return self._ConvertFileProtoToFileDescriptor(file_proto) + + def _ConvertFileProtoToFileDescriptor(self, file_proto): + """Creates a FileDescriptor from a proto or returns a cached copy. + + This method also has the side effect of loading all the symbols found in + the file into the appropriate dictionaries in the pool. + + Args: + file_proto: The proto to convert. + + Returns: + A FileDescriptor matching the passed in proto. + """ + if file_proto.name not in self._file_descriptors: + built_deps = list(self._GetDeps(file_proto.dependency)) + direct_deps = [self.FindFileByName(n) for n in file_proto.dependency] + public_deps = [direct_deps[i] for i in file_proto.public_dependency] + + file_descriptor = descriptor.FileDescriptor( + pool=self, + name=file_proto.name, + package=file_proto.package, + syntax=file_proto.syntax, + options=_OptionsOrNone(file_proto), + serialized_pb=file_proto.SerializeToString(), + dependencies=direct_deps, + public_dependencies=public_deps, + # pylint: disable=protected-access + create_key=descriptor._internal_create_key) + scope = {} + + # This loop extracts all the message and enum types from all the + # dependencies of the file_proto. This is necessary to create the + # scope of available message types when defining the passed in + # file proto. + for dependency in built_deps: + scope.update(self._ExtractSymbols( + dependency.message_types_by_name.values())) + scope.update((_PrefixWithDot(enum.full_name), enum) + for enum in dependency.enum_types_by_name.values()) + + for message_type in file_proto.message_type: + message_desc = self._ConvertMessageDescriptor( + message_type, file_proto.package, file_descriptor, scope, + file_proto.syntax) + file_descriptor.message_types_by_name[message_desc.name] = ( + message_desc) + + for enum_type in file_proto.enum_type: + file_descriptor.enum_types_by_name[enum_type.name] = ( + self._ConvertEnumDescriptor(enum_type, file_proto.package, + file_descriptor, None, scope, True)) + + for index, extension_proto in enumerate(file_proto.extension): + extension_desc = self._MakeFieldDescriptor( + extension_proto, file_proto.package, index, file_descriptor, + is_extension=True) + extension_desc.containing_type = self._GetTypeFromScope( + file_descriptor.package, extension_proto.extendee, scope) + self._SetFieldType(extension_proto, extension_desc, + file_descriptor.package, scope) + file_descriptor.extensions_by_name[extension_desc.name] = ( + extension_desc) + self._file_desc_by_toplevel_extension[extension_desc.full_name] = ( + file_descriptor) + + for desc_proto in file_proto.message_type: + self._SetAllFieldTypes(file_proto.package, desc_proto, scope) + + if file_proto.package: + desc_proto_prefix = _PrefixWithDot(file_proto.package) + else: + desc_proto_prefix = '' + + for desc_proto in file_proto.message_type: + desc = self._GetTypeFromScope( + desc_proto_prefix, desc_proto.name, scope) + file_descriptor.message_types_by_name[desc_proto.name] = desc + + for index, service_proto in enumerate(file_proto.service): + file_descriptor.services_by_name[service_proto.name] = ( + self._MakeServiceDescriptor(service_proto, index, scope, + file_proto.package, file_descriptor)) + + self._file_descriptors[file_proto.name] = file_descriptor + + # Add extensions to the pool + file_desc = self._file_descriptors[file_proto.name] + for extension in file_desc.extensions_by_name.values(): + self._AddExtensionDescriptor(extension) + for message_type in file_desc.message_types_by_name.values(): + for extension in message_type.extensions: + self._AddExtensionDescriptor(extension) + + return file_desc + + def _ConvertMessageDescriptor(self, desc_proto, package=None, file_desc=None, + scope=None, syntax=None): + """Adds the proto to the pool in the specified package. + + Args: + desc_proto: The descriptor_pb2.DescriptorProto protobuf message. + package: The package the proto should be located in. + file_desc: The file containing this message. + scope: Dict mapping short and full symbols to message and enum types. + syntax: string indicating syntax of the file ("proto2" or "proto3") + + Returns: + The added descriptor. + """ + + if package: + desc_name = '.'.join((package, desc_proto.name)) + else: + desc_name = desc_proto.name + + if file_desc is None: + file_name = None + else: + file_name = file_desc.name + + if scope is None: + scope = {} + + nested = [ + self._ConvertMessageDescriptor( + nested, desc_name, file_desc, scope, syntax) + for nested in desc_proto.nested_type] + enums = [ + self._ConvertEnumDescriptor(enum, desc_name, file_desc, None, + scope, False) + for enum in desc_proto.enum_type] + fields = [self._MakeFieldDescriptor(field, desc_name, index, file_desc) + for index, field in enumerate(desc_proto.field)] + extensions = [ + self._MakeFieldDescriptor(extension, desc_name, index, file_desc, + is_extension=True) + for index, extension in enumerate(desc_proto.extension)] + oneofs = [ + # pylint: disable=g-complex-comprehension + descriptor.OneofDescriptor( + desc.name, + '.'.join((desc_name, desc.name)), + index, + None, + [], + _OptionsOrNone(desc), + # pylint: disable=protected-access + create_key=descriptor._internal_create_key) + for index, desc in enumerate(desc_proto.oneof_decl) + ] + extension_ranges = [(r.start, r.end) for r in desc_proto.extension_range] + if extension_ranges: + is_extendable = True + else: + is_extendable = False + desc = descriptor.Descriptor( + name=desc_proto.name, + full_name=desc_name, + filename=file_name, + containing_type=None, + fields=fields, + oneofs=oneofs, + nested_types=nested, + enum_types=enums, + extensions=extensions, + options=_OptionsOrNone(desc_proto), + is_extendable=is_extendable, + extension_ranges=extension_ranges, + file=file_desc, + serialized_start=None, + serialized_end=None, + syntax=syntax, + # pylint: disable=protected-access + create_key=descriptor._internal_create_key) + for nested in desc.nested_types: + nested.containing_type = desc + for enum in desc.enum_types: + enum.containing_type = desc + for field_index, field_desc in enumerate(desc_proto.field): + if field_desc.HasField('oneof_index'): + oneof_index = field_desc.oneof_index + oneofs[oneof_index].fields.append(fields[field_index]) + fields[field_index].containing_oneof = oneofs[oneof_index] + + scope[_PrefixWithDot(desc_name)] = desc + self._CheckConflictRegister(desc, desc.full_name, desc.file.name) + self._descriptors[desc_name] = desc + return desc + + def _ConvertEnumDescriptor(self, enum_proto, package=None, file_desc=None, + containing_type=None, scope=None, top_level=False): + """Make a protobuf EnumDescriptor given an EnumDescriptorProto protobuf. + + Args: + enum_proto: The descriptor_pb2.EnumDescriptorProto protobuf message. + package: Optional package name for the new message EnumDescriptor. + file_desc: The file containing the enum descriptor. + containing_type: The type containing this enum. + scope: Scope containing available types. + top_level: If True, the enum is a top level symbol. If False, the enum + is defined inside a message. + + Returns: + The added descriptor + """ + + if package: + enum_name = '.'.join((package, enum_proto.name)) + else: + enum_name = enum_proto.name + + if file_desc is None: + file_name = None + else: + file_name = file_desc.name + + values = [self._MakeEnumValueDescriptor(value, index) + for index, value in enumerate(enum_proto.value)] + desc = descriptor.EnumDescriptor(name=enum_proto.name, + full_name=enum_name, + filename=file_name, + file=file_desc, + values=values, + containing_type=containing_type, + options=_OptionsOrNone(enum_proto), + # pylint: disable=protected-access + create_key=descriptor._internal_create_key) + scope['.%s' % enum_name] = desc + self._CheckConflictRegister(desc, desc.full_name, desc.file.name) + self._enum_descriptors[enum_name] = desc + + # Add top level enum values. + if top_level: + for value in values: + full_name = _NormalizeFullyQualifiedName( + '.'.join((package, value.name))) + self._CheckConflictRegister(value, full_name, file_name) + self._top_enum_values[full_name] = value + + return desc + + def _MakeFieldDescriptor(self, field_proto, message_name, index, + file_desc, is_extension=False): + """Creates a field descriptor from a FieldDescriptorProto. + + For message and enum type fields, this method will do a look up + in the pool for the appropriate descriptor for that type. If it + is unavailable, it will fall back to the _source function to + create it. If this type is still unavailable, construction will + fail. + + Args: + field_proto: The proto describing the field. + message_name: The name of the containing message. + index: Index of the field + file_desc: The file containing the field descriptor. + is_extension: Indication that this field is for an extension. + + Returns: + An initialized FieldDescriptor object + """ + + if message_name: + full_name = '.'.join((message_name, field_proto.name)) + else: + full_name = field_proto.name + + if field_proto.json_name: + json_name = field_proto.json_name + else: + json_name = None + + return descriptor.FieldDescriptor( + name=field_proto.name, + full_name=full_name, + index=index, + number=field_proto.number, + type=field_proto.type, + cpp_type=None, + message_type=None, + enum_type=None, + containing_type=None, + label=field_proto.label, + has_default_value=False, + default_value=None, + is_extension=is_extension, + extension_scope=None, + options=_OptionsOrNone(field_proto), + json_name=json_name, + file=file_desc, + # pylint: disable=protected-access + create_key=descriptor._internal_create_key) + + def _SetAllFieldTypes(self, package, desc_proto, scope): + """Sets all the descriptor's fields's types. + + This method also sets the containing types on any extensions. + + Args: + package: The current package of desc_proto. + desc_proto: The message descriptor to update. + scope: Enclosing scope of available types. + """ + + package = _PrefixWithDot(package) + + main_desc = self._GetTypeFromScope(package, desc_proto.name, scope) + + if package == '.': + nested_package = _PrefixWithDot(desc_proto.name) + else: + nested_package = '.'.join([package, desc_proto.name]) + + for field_proto, field_desc in zip(desc_proto.field, main_desc.fields): + self._SetFieldType(field_proto, field_desc, nested_package, scope) + + for extension_proto, extension_desc in ( + zip(desc_proto.extension, main_desc.extensions)): + extension_desc.containing_type = self._GetTypeFromScope( + nested_package, extension_proto.extendee, scope) + self._SetFieldType(extension_proto, extension_desc, nested_package, scope) + + for nested_type in desc_proto.nested_type: + self._SetAllFieldTypes(nested_package, nested_type, scope) + + def _SetFieldType(self, field_proto, field_desc, package, scope): + """Sets the field's type, cpp_type, message_type and enum_type. + + Args: + field_proto: Data about the field in proto format. + field_desc: The descriptor to modify. + package: The package the field's container is in. + scope: Enclosing scope of available types. + """ + if field_proto.type_name: + desc = self._GetTypeFromScope(package, field_proto.type_name, scope) + else: + desc = None + + if not field_proto.HasField('type'): + if isinstance(desc, descriptor.Descriptor): + field_proto.type = descriptor.FieldDescriptor.TYPE_MESSAGE + else: + field_proto.type = descriptor.FieldDescriptor.TYPE_ENUM + + field_desc.cpp_type = descriptor.FieldDescriptor.ProtoTypeToCppProtoType( + field_proto.type) + + if (field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE + or field_proto.type == descriptor.FieldDescriptor.TYPE_GROUP): + field_desc.message_type = desc + + if field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM: + field_desc.enum_type = desc + + if field_proto.label == descriptor.FieldDescriptor.LABEL_REPEATED: + field_desc.has_default_value = False + field_desc.default_value = [] + elif field_proto.HasField('default_value'): + field_desc.has_default_value = True + if (field_proto.type == descriptor.FieldDescriptor.TYPE_DOUBLE or + field_proto.type == descriptor.FieldDescriptor.TYPE_FLOAT): + field_desc.default_value = float(field_proto.default_value) + elif field_proto.type == descriptor.FieldDescriptor.TYPE_STRING: + field_desc.default_value = field_proto.default_value + elif field_proto.type == descriptor.FieldDescriptor.TYPE_BOOL: + field_desc.default_value = field_proto.default_value.lower() == 'true' + elif field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM: + field_desc.default_value = field_desc.enum_type.values_by_name[ + field_proto.default_value].number + elif field_proto.type == descriptor.FieldDescriptor.TYPE_BYTES: + field_desc.default_value = text_encoding.CUnescape( + field_proto.default_value) + elif field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE: + field_desc.default_value = None + else: + # All other types are of the "int" type. + field_desc.default_value = int(field_proto.default_value) + else: + field_desc.has_default_value = False + if (field_proto.type == descriptor.FieldDescriptor.TYPE_DOUBLE or + field_proto.type == descriptor.FieldDescriptor.TYPE_FLOAT): + field_desc.default_value = 0.0 + elif field_proto.type == descriptor.FieldDescriptor.TYPE_STRING: + field_desc.default_value = u'' + elif field_proto.type == descriptor.FieldDescriptor.TYPE_BOOL: + field_desc.default_value = False + elif field_proto.type == descriptor.FieldDescriptor.TYPE_ENUM: + field_desc.default_value = field_desc.enum_type.values[0].number + elif field_proto.type == descriptor.FieldDescriptor.TYPE_BYTES: + field_desc.default_value = b'' + elif field_proto.type == descriptor.FieldDescriptor.TYPE_MESSAGE: + field_desc.default_value = None + elif field_proto.type == descriptor.FieldDescriptor.TYPE_GROUP: + field_desc.default_value = None + else: + # All other types are of the "int" type. + field_desc.default_value = 0 + + field_desc.type = field_proto.type + + def _MakeEnumValueDescriptor(self, value_proto, index): + """Creates a enum value descriptor object from a enum value proto. + + Args: + value_proto: The proto describing the enum value. + index: The index of the enum value. + + Returns: + An initialized EnumValueDescriptor object. + """ + + return descriptor.EnumValueDescriptor( + name=value_proto.name, + index=index, + number=value_proto.number, + options=_OptionsOrNone(value_proto), + type=None, + # pylint: disable=protected-access + create_key=descriptor._internal_create_key) + + def _MakeServiceDescriptor(self, service_proto, service_index, scope, + package, file_desc): + """Make a protobuf ServiceDescriptor given a ServiceDescriptorProto. + + Args: + service_proto: The descriptor_pb2.ServiceDescriptorProto protobuf message. + service_index: The index of the service in the File. + scope: Dict mapping short and full symbols to message and enum types. + package: Optional package name for the new message EnumDescriptor. + file_desc: The file containing the service descriptor. + + Returns: + The added descriptor. + """ + + if package: + service_name = '.'.join((package, service_proto.name)) + else: + service_name = service_proto.name + + methods = [self._MakeMethodDescriptor(method_proto, service_name, package, + scope, index) + for index, method_proto in enumerate(service_proto.method)] + desc = descriptor.ServiceDescriptor( + name=service_proto.name, + full_name=service_name, + index=service_index, + methods=methods, + options=_OptionsOrNone(service_proto), + file=file_desc, + # pylint: disable=protected-access + create_key=descriptor._internal_create_key) + self._CheckConflictRegister(desc, desc.full_name, desc.file.name) + self._service_descriptors[service_name] = desc + return desc + + def _MakeMethodDescriptor(self, method_proto, service_name, package, scope, + index): + """Creates a method descriptor from a MethodDescriptorProto. + + Args: + method_proto: The proto describing the method. + service_name: The name of the containing service. + package: Optional package name to look up for types. + scope: Scope containing available types. + index: Index of the method in the service. + + Returns: + An initialized MethodDescriptor object. + """ + full_name = '.'.join((service_name, method_proto.name)) + input_type = self._GetTypeFromScope( + package, method_proto.input_type, scope) + output_type = self._GetTypeFromScope( + package, method_proto.output_type, scope) + return descriptor.MethodDescriptor( + name=method_proto.name, + full_name=full_name, + index=index, + containing_service=None, + input_type=input_type, + output_type=output_type, + client_streaming=method_proto.client_streaming, + server_streaming=method_proto.server_streaming, + options=_OptionsOrNone(method_proto), + # pylint: disable=protected-access + create_key=descriptor._internal_create_key) + + def _ExtractSymbols(self, descriptors): + """Pulls out all the symbols from descriptor protos. + + Args: + descriptors: The messages to extract descriptors from. + Yields: + A two element tuple of the type name and descriptor object. + """ + + for desc in descriptors: + yield (_PrefixWithDot(desc.full_name), desc) + for symbol in self._ExtractSymbols(desc.nested_types): + yield symbol + for enum in desc.enum_types: + yield (_PrefixWithDot(enum.full_name), enum) + + def _GetDeps(self, dependencies, visited=None): + """Recursively finds dependencies for file protos. + + Args: + dependencies: The names of the files being depended on. + visited: The names of files already found. + + Yields: + Each direct and indirect dependency. + """ + + visited = visited or set() + for dependency in dependencies: + if dependency not in visited: + visited.add(dependency) + dep_desc = self.FindFileByName(dependency) + yield dep_desc + public_files = [d.name for d in dep_desc.public_dependencies] + yield from self._GetDeps(public_files, visited) + + def _GetTypeFromScope(self, package, type_name, scope): + """Finds a given type name in the current scope. + + Args: + package: The package the proto should be located in. + type_name: The name of the type to be found in the scope. + scope: Dict mapping short and full symbols to message and enum types. + + Returns: + The descriptor for the requested type. + """ + if type_name not in scope: + components = _PrefixWithDot(package).split('.') + while components: + possible_match = '.'.join(components + [type_name]) + if possible_match in scope: + type_name = possible_match + break + else: + components.pop(-1) + return scope[type_name] + + +def _PrefixWithDot(name): + return name if name.startswith('.') else '.%s' % name + + +if _USE_C_DESCRIPTORS: + # TODO(amauryfa): This pool could be constructed from Python code, when we + # support a flag like 'use_cpp_generated_pool=True'. + # pylint: disable=protected-access + _DEFAULT = descriptor._message.default_pool +else: + _DEFAULT = DescriptorPool() + + +def Default(): + return _DEFAULT diff --git a/lib/protobuf/duration_pb2.py b/lib/protobuf/duration_pb2.py new file mode 100644 index 0000000..a8ecc07 --- /dev/null +++ b/lib/protobuf/duration_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/duration.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\x1egoogle/protobuf/duration.proto\x12\x0fgoogle.protobuf\"*\n\x08\x44uration\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x12\r\n\x05nanos\x18\x02 \x01(\x05\x42\x83\x01\n\x13\x63om.google.protobufB\rDurationProtoP\x01Z1google.golang.org/protobuf/types/known/durationpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.duration_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\023com.google.protobufB\rDurationProtoP\001Z1google.golang.org/protobuf/types/known/durationpb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _DURATION._serialized_start=51 + _DURATION._serialized_end=93 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/empty_pb2.py b/lib/protobuf/empty_pb2.py new file mode 100644 index 0000000..0b4d554 --- /dev/null +++ b/lib/protobuf/empty_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/empty.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\x1bgoogle/protobuf/empty.proto\x12\x0fgoogle.protobuf\"\x07\n\x05\x45mptyB}\n\x13\x63om.google.protobufB\nEmptyProtoP\x01Z.google.golang.org/protobuf/types/known/emptypb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.empty_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\023com.google.protobufB\nEmptyProtoP\001Z.google.golang.org/protobuf/types/known/emptypb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _EMPTY._serialized_start=48 + _EMPTY._serialized_end=55 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/field_mask_pb2.py b/lib/protobuf/field_mask_pb2.py new file mode 100644 index 0000000..80a4e96 --- /dev/null +++ b/lib/protobuf/field_mask_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/field_mask.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 google/protobuf/field_mask.proto\x12\x0fgoogle.protobuf\"\x1a\n\tFieldMask\x12\r\n\x05paths\x18\x01 \x03(\tB\x85\x01\n\x13\x63om.google.protobufB\x0e\x46ieldMaskProtoP\x01Z2google.golang.org/protobuf/types/known/fieldmaskpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.field_mask_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\023com.google.protobufB\016FieldMaskProtoP\001Z2google.golang.org/protobuf/types/known/fieldmaskpb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _FIELDMASK._serialized_start=53 + _FIELDMASK._serialized_end=79 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/internal/__init__.py b/lib/protobuf/internal/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/protobuf/internal/_api_implementation.cpython-310-x86_64-linux-gnu.so b/lib/protobuf/internal/_api_implementation.cpython-310-x86_64-linux-gnu.so new file mode 100755 index 0000000..ba004e2 Binary files /dev/null and b/lib/protobuf/internal/_api_implementation.cpython-310-x86_64-linux-gnu.so differ diff --git a/lib/protobuf/internal/api_implementation.py b/lib/protobuf/internal/api_implementation.py new file mode 100644 index 0000000..7fef237 --- /dev/null +++ b/lib/protobuf/internal/api_implementation.py @@ -0,0 +1,112 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Determine which implementation of the protobuf API is used in this process. +""" + +import os +import sys +import warnings + +try: + # pylint: disable=g-import-not-at-top + from google.protobuf.internal import _api_implementation + # The compile-time constants in the _api_implementation module can be used to + # switch to a certain implementation of the Python API at build time. + _api_version = _api_implementation.api_version +except ImportError: + _api_version = -1 # Unspecified by compiler flags. + +if _api_version == 1: + raise ValueError('api_version=1 is no longer supported.') + + +_default_implementation_type = ('cpp' if _api_version > 0 else 'python') + + +# This environment variable can be used to switch to a certain implementation +# of the Python API, overriding the compile-time constants in the +# _api_implementation module. Right now only 'python' and 'cpp' are valid +# values. Any other value will be ignored. +_implementation_type = os.getenv('PROTOCOL_BUFFERS_PYTHON_IMPLEMENTATION', + _default_implementation_type) + +if _implementation_type != 'python': + _implementation_type = 'cpp' + +if 'PyPy' in sys.version and _implementation_type == 'cpp': + warnings.warn('PyPy does not work yet with cpp protocol buffers. ' + 'Falling back to the python implementation.') + _implementation_type = 'python' + + +# Detect if serialization should be deterministic by default +try: + # The presence of this module in a build allows the proto implementation to + # be upgraded merely via build deps. + # + # NOTE: Merely importing this automatically enables deterministic proto + # serialization for C++ code, but we still need to export it as a boolean so + # that we can do the same for `_implementation_type == 'python'`. + # + # NOTE2: It is possible for C++ code to enable deterministic serialization by + # default _without_ affecting Python code, if the C++ implementation is not in + # use by this module. That is intended behavior, so we don't actually expose + # this boolean outside of this module. + # + # pylint: disable=g-import-not-at-top,unused-import + from google.protobuf import enable_deterministic_proto_serialization + _python_deterministic_proto_serialization = True +except ImportError: + _python_deterministic_proto_serialization = False + + +# Usage of this function is discouraged. Clients shouldn't care which +# implementation of the API is in use. Note that there is no guarantee +# that differences between APIs will be maintained. +# Please don't use this function if possible. +def Type(): + return _implementation_type + + +def _SetType(implementation_type): + """Never use! Only for protobuf benchmark.""" + global _implementation_type + _implementation_type = implementation_type + + +# See comment on 'Type' above. +def Version(): + return 2 + + +# For internal use only +def IsPythonDefaultSerializationDeterministic(): + return _python_deterministic_proto_serialization diff --git a/lib/protobuf/internal/builder.py b/lib/protobuf/internal/builder.py new file mode 100644 index 0000000..64353ee --- /dev/null +++ b/lib/protobuf/internal/builder.py @@ -0,0 +1,130 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Builds descriptors, message classes and services for generated _pb2.py. + +This file is only called in python generated _pb2.py files. It builds +descriptors, message classes and services that users can directly use +in generated code. +""" + +__author__ = 'jieluo@google.com (Jie Luo)' + +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +_sym_db = _symbol_database.Default() + + +def BuildMessageAndEnumDescriptors(file_des, module): + """Builds message and enum descriptors. + + Args: + file_des: FileDescriptor of the .proto file + module: Generated _pb2 module + """ + + def BuildNestedDescriptors(msg_des, prefix): + for (name, nested_msg) in msg_des.nested_types_by_name.items(): + module_name = prefix + name.upper() + module[module_name] = nested_msg + BuildNestedDescriptors(nested_msg, module_name + '_') + for enum_des in msg_des.enum_types: + module[prefix + enum_des.name.upper()] = enum_des + + for (name, msg_des) in file_des.message_types_by_name.items(): + module_name = '_' + name.upper() + module[module_name] = msg_des + BuildNestedDescriptors(msg_des, module_name + '_') + + +def BuildTopDescriptorsAndMessages(file_des, module_name, module): + """Builds top level descriptors and message classes. + + Args: + file_des: FileDescriptor of the .proto file + module_name: str, the name of generated _pb2 module + module: Generated _pb2 module + """ + + def BuildMessage(msg_des): + create_dict = {} + for (name, nested_msg) in msg_des.nested_types_by_name.items(): + create_dict[name] = BuildMessage(nested_msg) + create_dict['DESCRIPTOR'] = msg_des + create_dict['__module__'] = module_name + message_class = _reflection.GeneratedProtocolMessageType( + msg_des.name, (_message.Message,), create_dict) + _sym_db.RegisterMessage(message_class) + return message_class + + # top level enums + for (name, enum_des) in file_des.enum_types_by_name.items(): + module['_' + name.upper()] = enum_des + module[name] = enum_type_wrapper.EnumTypeWrapper(enum_des) + for enum_value in enum_des.values: + module[enum_value.name] = enum_value.number + + # top level extensions + for (name, extension_des) in file_des.extensions_by_name.items(): + module[name.upper() + '_FIELD_NUMBER'] = extension_des.number + module[name] = extension_des + + # services + for (name, service) in file_des.services_by_name.items(): + module['_' + name.upper()] = service + + # Build messages. + for (name, msg_des) in file_des.message_types_by_name.items(): + module[name] = BuildMessage(msg_des) + + +def BuildServices(file_des, module_name, module): + """Builds services classes and services stub class. + + Args: + file_des: FileDescriptor of the .proto file + module_name: str, the name of generated _pb2 module + module: Generated _pb2 module + """ + # pylint: disable=g-import-not-at-top + from google.protobuf import service as _service + from google.protobuf import service_reflection + # pylint: enable=g-import-not-at-top + for (name, service) in file_des.services_by_name.items(): + module[name] = service_reflection.GeneratedServiceType( + name, (_service.Service,), + dict(DESCRIPTOR=service, __module__=module_name)) + stub_name = name + '_Stub' + module[stub_name] = service_reflection.GeneratedServiceStubType( + stub_name, (module[name],), + dict(DESCRIPTOR=service, __module__=module_name)) diff --git a/lib/protobuf/internal/containers.py b/lib/protobuf/internal/containers.py new file mode 100644 index 0000000..29fbb53 --- /dev/null +++ b/lib/protobuf/internal/containers.py @@ -0,0 +1,710 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Contains container classes to represent different protocol buffer types. + +This file defines container classes which represent categories of protocol +buffer field types which need extra maintenance. Currently these categories +are: + +- Repeated scalar fields - These are all repeated fields which aren't + composite (e.g. they are of simple types like int32, string, etc). +- Repeated composite fields - Repeated fields which are composite. This + includes groups and nested messages. +""" + +import collections.abc +import copy +import pickle +from typing import ( + Any, + Iterable, + Iterator, + List, + MutableMapping, + MutableSequence, + NoReturn, + Optional, + Sequence, + TypeVar, + Union, + overload, +) + + +_T = TypeVar('_T') +_K = TypeVar('_K') +_V = TypeVar('_V') + + +class BaseContainer(Sequence[_T]): + """Base container class.""" + + # Minimizes memory usage and disallows assignment to other attributes. + __slots__ = ['_message_listener', '_values'] + + def __init__(self, message_listener: Any) -> None: + """ + Args: + message_listener: A MessageListener implementation. + The RepeatedScalarFieldContainer will call this object's + Modified() method when it is modified. + """ + self._message_listener = message_listener + self._values = [] + + @overload + def __getitem__(self, key: int) -> _T: + ... + + @overload + def __getitem__(self, key: slice) -> List[_T]: + ... + + def __getitem__(self, key): + """Retrieves item by the specified key.""" + return self._values[key] + + def __len__(self) -> int: + """Returns the number of elements in the container.""" + return len(self._values) + + def __ne__(self, other: Any) -> bool: + """Checks if another instance isn't equal to this one.""" + # The concrete classes should define __eq__. + return not self == other + + __hash__ = None + + def __repr__(self) -> str: + return repr(self._values) + + def sort(self, *args, **kwargs) -> None: + # Continue to support the old sort_function keyword argument. + # This is expected to be a rare occurrence, so use LBYL to avoid + # the overhead of actually catching KeyError. + if 'sort_function' in kwargs: + kwargs['cmp'] = kwargs.pop('sort_function') + self._values.sort(*args, **kwargs) + + def reverse(self) -> None: + self._values.reverse() + + +# TODO(slebedev): Remove this. BaseContainer does *not* conform to +# MutableSequence, only its subclasses do. +collections.abc.MutableSequence.register(BaseContainer) + + +class RepeatedScalarFieldContainer(BaseContainer[_T], MutableSequence[_T]): + """Simple, type-checked, list-like container for holding repeated scalars.""" + + # Disallows assignment to other attributes. + __slots__ = ['_type_checker'] + + def __init__( + self, + message_listener: Any, + type_checker: Any, + ) -> None: + """Args: + + message_listener: A MessageListener implementation. The + RepeatedScalarFieldContainer will call this object's Modified() method + when it is modified. + type_checker: A type_checkers.ValueChecker instance to run on elements + inserted into this container. + """ + super().__init__(message_listener) + self._type_checker = type_checker + + def append(self, value: _T) -> None: + """Appends an item to the list. Similar to list.append().""" + self._values.append(self._type_checker.CheckValue(value)) + if not self._message_listener.dirty: + self._message_listener.Modified() + + def insert(self, key: int, value: _T) -> None: + """Inserts the item at the specified position. Similar to list.insert().""" + self._values.insert(key, self._type_checker.CheckValue(value)) + if not self._message_listener.dirty: + self._message_listener.Modified() + + def extend(self, elem_seq: Iterable[_T]) -> None: + """Extends by appending the given iterable. Similar to list.extend().""" + if elem_seq is None: + return + try: + elem_seq_iter = iter(elem_seq) + except TypeError: + if not elem_seq: + # silently ignore falsy inputs :-/. + # TODO(ptucker): Deprecate this behavior. b/18413862 + return + raise + + new_values = [self._type_checker.CheckValue(elem) for elem in elem_seq_iter] + if new_values: + self._values.extend(new_values) + self._message_listener.Modified() + + def MergeFrom( + self, + other: Union['RepeatedScalarFieldContainer[_T]', Iterable[_T]], + ) -> None: + """Appends the contents of another repeated field of the same type to this + one. We do not check the types of the individual fields. + """ + self._values.extend(other) + self._message_listener.Modified() + + def remove(self, elem: _T): + """Removes an item from the list. Similar to list.remove().""" + self._values.remove(elem) + self._message_listener.Modified() + + def pop(self, key: Optional[int] = -1) -> _T: + """Removes and returns an item at a given index. Similar to list.pop().""" + value = self._values[key] + self.__delitem__(key) + return value + + @overload + def __setitem__(self, key: int, value: _T) -> None: + ... + + @overload + def __setitem__(self, key: slice, value: Iterable[_T]) -> None: + ... + + def __setitem__(self, key, value) -> None: + """Sets the item on the specified position.""" + if isinstance(key, slice): + if key.step is not None: + raise ValueError('Extended slices not supported') + self._values[key] = map(self._type_checker.CheckValue, value) + self._message_listener.Modified() + else: + self._values[key] = self._type_checker.CheckValue(value) + self._message_listener.Modified() + + def __delitem__(self, key: Union[int, slice]) -> None: + """Deletes the item at the specified position.""" + del self._values[key] + self._message_listener.Modified() + + def __eq__(self, other: Any) -> bool: + """Compares the current instance with another one.""" + if self is other: + return True + # Special case for the same type which should be common and fast. + if isinstance(other, self.__class__): + return other._values == self._values + # We are presumably comparing against some other sequence type. + return other == self._values + + def __deepcopy__( + self, + unused_memo: Any = None, + ) -> 'RepeatedScalarFieldContainer[_T]': + clone = RepeatedScalarFieldContainer( + copy.deepcopy(self._message_listener), self._type_checker) + clone.MergeFrom(self) + return clone + + def __reduce__(self, **kwargs) -> NoReturn: + raise pickle.PickleError( + "Can't pickle repeated scalar fields, convert to list first") + + +# TODO(slebedev): Constrain T to be a subtype of Message. +class RepeatedCompositeFieldContainer(BaseContainer[_T], MutableSequence[_T]): + """Simple, list-like container for holding repeated composite fields.""" + + # Disallows assignment to other attributes. + __slots__ = ['_message_descriptor'] + + def __init__(self, message_listener: Any, message_descriptor: Any) -> None: + """ + Note that we pass in a descriptor instead of the generated directly, + since at the time we construct a _RepeatedCompositeFieldContainer we + haven't yet necessarily initialized the type that will be contained in the + container. + + Args: + message_listener: A MessageListener implementation. + The RepeatedCompositeFieldContainer will call this object's + Modified() method when it is modified. + message_descriptor: A Descriptor instance describing the protocol type + that should be present in this container. We'll use the + _concrete_class field of this descriptor when the client calls add(). + """ + super().__init__(message_listener) + self._message_descriptor = message_descriptor + + def add(self, **kwargs: Any) -> _T: + """Adds a new element at the end of the list and returns it. Keyword + arguments may be used to initialize the element. + """ + new_element = self._message_descriptor._concrete_class(**kwargs) + new_element._SetListener(self._message_listener) + self._values.append(new_element) + if not self._message_listener.dirty: + self._message_listener.Modified() + return new_element + + def append(self, value: _T) -> None: + """Appends one element by copying the message.""" + new_element = self._message_descriptor._concrete_class() + new_element._SetListener(self._message_listener) + new_element.CopyFrom(value) + self._values.append(new_element) + if not self._message_listener.dirty: + self._message_listener.Modified() + + def insert(self, key: int, value: _T) -> None: + """Inserts the item at the specified position by copying.""" + new_element = self._message_descriptor._concrete_class() + new_element._SetListener(self._message_listener) + new_element.CopyFrom(value) + self._values.insert(key, new_element) + if not self._message_listener.dirty: + self._message_listener.Modified() + + def extend(self, elem_seq: Iterable[_T]) -> None: + """Extends by appending the given sequence of elements of the same type + + as this one, copying each individual message. + """ + message_class = self._message_descriptor._concrete_class + listener = self._message_listener + values = self._values + for message in elem_seq: + new_element = message_class() + new_element._SetListener(listener) + new_element.MergeFrom(message) + values.append(new_element) + listener.Modified() + + def MergeFrom( + self, + other: Union['RepeatedCompositeFieldContainer[_T]', Iterable[_T]], + ) -> None: + """Appends the contents of another repeated field of the same type to this + one, copying each individual message. + """ + self.extend(other) + + def remove(self, elem: _T) -> None: + """Removes an item from the list. Similar to list.remove().""" + self._values.remove(elem) + self._message_listener.Modified() + + def pop(self, key: Optional[int] = -1) -> _T: + """Removes and returns an item at a given index. Similar to list.pop().""" + value = self._values[key] + self.__delitem__(key) + return value + + @overload + def __setitem__(self, key: int, value: _T) -> None: + ... + + @overload + def __setitem__(self, key: slice, value: Iterable[_T]) -> None: + ... + + def __setitem__(self, key, value): + # This method is implemented to make RepeatedCompositeFieldContainer + # structurally compatible with typing.MutableSequence. It is + # otherwise unsupported and will always raise an error. + raise TypeError( + f'{self.__class__.__name__} object does not support item assignment') + + def __delitem__(self, key: Union[int, slice]) -> None: + """Deletes the item at the specified position.""" + del self._values[key] + self._message_listener.Modified() + + def __eq__(self, other: Any) -> bool: + """Compares the current instance with another one.""" + if self is other: + return True + if not isinstance(other, self.__class__): + raise TypeError('Can only compare repeated composite fields against ' + 'other repeated composite fields.') + return self._values == other._values + + +class ScalarMap(MutableMapping[_K, _V]): + """Simple, type-checked, dict-like container for holding repeated scalars.""" + + # Disallows assignment to other attributes. + __slots__ = ['_key_checker', '_value_checker', '_values', '_message_listener', + '_entry_descriptor'] + + def __init__( + self, + message_listener: Any, + key_checker: Any, + value_checker: Any, + entry_descriptor: Any, + ) -> None: + """ + Args: + message_listener: A MessageListener implementation. + The ScalarMap will call this object's Modified() method when it + is modified. + key_checker: A type_checkers.ValueChecker instance to run on keys + inserted into this container. + value_checker: A type_checkers.ValueChecker instance to run on values + inserted into this container. + entry_descriptor: The MessageDescriptor of a map entry: key and value. + """ + self._message_listener = message_listener + self._key_checker = key_checker + self._value_checker = value_checker + self._entry_descriptor = entry_descriptor + self._values = {} + + def __getitem__(self, key: _K) -> _V: + try: + return self._values[key] + except KeyError: + key = self._key_checker.CheckValue(key) + val = self._value_checker.DefaultValue() + self._values[key] = val + return val + + def __contains__(self, item: _K) -> bool: + # We check the key's type to match the strong-typing flavor of the API. + # Also this makes it easier to match the behavior of the C++ implementation. + self._key_checker.CheckValue(item) + return item in self._values + + @overload + def get(self, key: _K) -> Optional[_V]: + ... + + @overload + def get(self, key: _K, default: _T) -> Union[_V, _T]: + ... + + # We need to override this explicitly, because our defaultdict-like behavior + # will make the default implementation (from our base class) always insert + # the key. + def get(self, key, default=None): + if key in self: + return self[key] + else: + return default + + def __setitem__(self, key: _K, value: _V) -> _T: + checked_key = self._key_checker.CheckValue(key) + checked_value = self._value_checker.CheckValue(value) + self._values[checked_key] = checked_value + self._message_listener.Modified() + + def __delitem__(self, key: _K) -> None: + del self._values[key] + self._message_listener.Modified() + + def __len__(self) -> int: + return len(self._values) + + def __iter__(self) -> Iterator[_K]: + return iter(self._values) + + def __repr__(self) -> str: + return repr(self._values) + + def MergeFrom(self, other: 'ScalarMap[_K, _V]') -> None: + self._values.update(other._values) + self._message_listener.Modified() + + def InvalidateIterators(self) -> None: + # It appears that the only way to reliably invalidate iterators to + # self._values is to ensure that its size changes. + original = self._values + self._values = original.copy() + original[None] = None + + # This is defined in the abstract base, but we can do it much more cheaply. + def clear(self) -> None: + self._values.clear() + self._message_listener.Modified() + + def GetEntryClass(self) -> Any: + return self._entry_descriptor._concrete_class + + +class MessageMap(MutableMapping[_K, _V]): + """Simple, type-checked, dict-like container for with submessage values.""" + + # Disallows assignment to other attributes. + __slots__ = ['_key_checker', '_values', '_message_listener', + '_message_descriptor', '_entry_descriptor'] + + def __init__( + self, + message_listener: Any, + message_descriptor: Any, + key_checker: Any, + entry_descriptor: Any, + ) -> None: + """ + Args: + message_listener: A MessageListener implementation. + The ScalarMap will call this object's Modified() method when it + is modified. + key_checker: A type_checkers.ValueChecker instance to run on keys + inserted into this container. + value_checker: A type_checkers.ValueChecker instance to run on values + inserted into this container. + entry_descriptor: The MessageDescriptor of a map entry: key and value. + """ + self._message_listener = message_listener + self._message_descriptor = message_descriptor + self._key_checker = key_checker + self._entry_descriptor = entry_descriptor + self._values = {} + + def __getitem__(self, key: _K) -> _V: + key = self._key_checker.CheckValue(key) + try: + return self._values[key] + except KeyError: + new_element = self._message_descriptor._concrete_class() + new_element._SetListener(self._message_listener) + self._values[key] = new_element + self._message_listener.Modified() + return new_element + + def get_or_create(self, key: _K) -> _V: + """get_or_create() is an alias for getitem (ie. map[key]). + + Args: + key: The key to get or create in the map. + + This is useful in cases where you want to be explicit that the call is + mutating the map. This can avoid lint errors for statements like this + that otherwise would appear to be pointless statements: + + msg.my_map[key] + """ + return self[key] + + @overload + def get(self, key: _K) -> Optional[_V]: + ... + + @overload + def get(self, key: _K, default: _T) -> Union[_V, _T]: + ... + + # We need to override this explicitly, because our defaultdict-like behavior + # will make the default implementation (from our base class) always insert + # the key. + def get(self, key, default=None): + if key in self: + return self[key] + else: + return default + + def __contains__(self, item: _K) -> bool: + item = self._key_checker.CheckValue(item) + return item in self._values + + def __setitem__(self, key: _K, value: _V) -> NoReturn: + raise ValueError('May not set values directly, call my_map[key].foo = 5') + + def __delitem__(self, key: _K) -> None: + key = self._key_checker.CheckValue(key) + del self._values[key] + self._message_listener.Modified() + + def __len__(self) -> int: + return len(self._values) + + def __iter__(self) -> Iterator[_K]: + return iter(self._values) + + def __repr__(self) -> str: + return repr(self._values) + + def MergeFrom(self, other: 'MessageMap[_K, _V]') -> None: + # pylint: disable=protected-access + for key in other._values: + # According to documentation: "When parsing from the wire or when merging, + # if there are duplicate map keys the last key seen is used". + if key in self: + del self[key] + self[key].CopyFrom(other[key]) + # self._message_listener.Modified() not required here, because + # mutations to submessages already propagate. + + def InvalidateIterators(self) -> None: + # It appears that the only way to reliably invalidate iterators to + # self._values is to ensure that its size changes. + original = self._values + self._values = original.copy() + original[None] = None + + # This is defined in the abstract base, but we can do it much more cheaply. + def clear(self) -> None: + self._values.clear() + self._message_listener.Modified() + + def GetEntryClass(self) -> Any: + return self._entry_descriptor._concrete_class + + +class _UnknownField: + """A parsed unknown field.""" + + # Disallows assignment to other attributes. + __slots__ = ['_field_number', '_wire_type', '_data'] + + def __init__(self, field_number, wire_type, data): + self._field_number = field_number + self._wire_type = wire_type + self._data = data + return + + def __lt__(self, other): + # pylint: disable=protected-access + return self._field_number < other._field_number + + def __eq__(self, other): + if self is other: + return True + # pylint: disable=protected-access + return (self._field_number == other._field_number and + self._wire_type == other._wire_type and + self._data == other._data) + + +class UnknownFieldRef: # pylint: disable=missing-class-docstring + + def __init__(self, parent, index): + self._parent = parent + self._index = index + + def _check_valid(self): + if not self._parent: + raise ValueError('UnknownField does not exist. ' + 'The parent message might be cleared.') + if self._index >= len(self._parent): + raise ValueError('UnknownField does not exist. ' + 'The parent message might be cleared.') + + @property + def field_number(self): + self._check_valid() + # pylint: disable=protected-access + return self._parent._internal_get(self._index)._field_number + + @property + def wire_type(self): + self._check_valid() + # pylint: disable=protected-access + return self._parent._internal_get(self._index)._wire_type + + @property + def data(self): + self._check_valid() + # pylint: disable=protected-access + return self._parent._internal_get(self._index)._data + + +class UnknownFieldSet: + """UnknownField container""" + + # Disallows assignment to other attributes. + __slots__ = ['_values'] + + def __init__(self): + self._values = [] + + def __getitem__(self, index): + if self._values is None: + raise ValueError('UnknownFields does not exist. ' + 'The parent message might be cleared.') + size = len(self._values) + if index < 0: + index += size + if index < 0 or index >= size: + raise IndexError('index %d out of range'.index) + + return UnknownFieldRef(self, index) + + def _internal_get(self, index): + return self._values[index] + + def __len__(self): + if self._values is None: + raise ValueError('UnknownFields does not exist. ' + 'The parent message might be cleared.') + return len(self._values) + + def _add(self, field_number, wire_type, data): + unknown_field = _UnknownField(field_number, wire_type, data) + self._values.append(unknown_field) + return unknown_field + + def __iter__(self): + for i in range(len(self)): + yield UnknownFieldRef(self, i) + + def _extend(self, other): + if other is None: + return + # pylint: disable=protected-access + self._values.extend(other._values) + + def __eq__(self, other): + if self is other: + return True + # Sort unknown fields because their order shouldn't + # affect equality test. + values = list(self._values) + if other is None: + return not values + values.sort() + # pylint: disable=protected-access + other_values = sorted(other._values) + return values == other_values + + def _clear(self): + for value in self._values: + # pylint: disable=protected-access + if isinstance(value._data, UnknownFieldSet): + value._data._clear() # pylint: disable=protected-access + self._values = None diff --git a/lib/protobuf/internal/decoder.py b/lib/protobuf/internal/decoder.py new file mode 100644 index 0000000..bc1b7b7 --- /dev/null +++ b/lib/protobuf/internal/decoder.py @@ -0,0 +1,1029 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Code for decoding protocol buffer primitives. + +This code is very similar to encoder.py -- read the docs for that module first. + +A "decoder" is a function with the signature: + Decode(buffer, pos, end, message, field_dict) +The arguments are: + buffer: The string containing the encoded message. + pos: The current position in the string. + end: The position in the string where the current message ends. May be + less than len(buffer) if we're reading a sub-message. + message: The message object into which we're parsing. + field_dict: message._fields (avoids a hashtable lookup). +The decoder reads the field and stores it into field_dict, returning the new +buffer position. A decoder for a repeated field may proactively decode all of +the elements of that field, if they appear consecutively. + +Note that decoders may throw any of the following: + IndexError: Indicates a truncated message. + struct.error: Unpacking of a fixed-width field failed. + message.DecodeError: Other errors. + +Decoders are expected to raise an exception if they are called with pos > end. +This allows callers to be lax about bounds checking: it's fineto read past +"end" as long as you are sure that someone else will notice and throw an +exception later on. + +Something up the call stack is expected to catch IndexError and struct.error +and convert them to message.DecodeError. + +Decoders are constructed using decoder constructors with the signature: + MakeDecoder(field_number, is_repeated, is_packed, key, new_default) +The arguments are: + field_number: The field number of the field we want to decode. + is_repeated: Is the field a repeated field? (bool) + is_packed: Is the field a packed field? (bool) + key: The key to use when looking up the field within field_dict. + (This is actually the FieldDescriptor but nothing in this + file should depend on that.) + new_default: A function which takes a message object as a parameter and + returns a new instance of the default value for this field. + (This is called for repeated fields and sub-messages, when an + instance does not already exist.) + +As with encoders, we define a decoder constructor for every type of field. +Then, for every field of every message class we construct an actual decoder. +That decoder goes into a dict indexed by tag, so when we decode a message +we repeatedly read a tag, look up the corresponding decoder, and invoke it. +""" + +__author__ = 'kenton@google.com (Kenton Varda)' + +import math +import struct + +from google.protobuf.internal import containers +from google.protobuf.internal import encoder +from google.protobuf.internal import wire_format +from google.protobuf import message + + +# This is not for optimization, but rather to avoid conflicts with local +# variables named "message". +_DecodeError = message.DecodeError + + +def _VarintDecoder(mask, result_type): + """Return an encoder for a basic varint value (does not include tag). + + Decoded values will be bitwise-anded with the given mask before being + returned, e.g. to limit them to 32 bits. The returned decoder does not + take the usual "end" parameter -- the caller is expected to do bounds checking + after the fact (often the caller can defer such checking until later). The + decoder returns a (value, new_pos) pair. + """ + + def DecodeVarint(buffer, pos): + result = 0 + shift = 0 + while 1: + b = buffer[pos] + result |= ((b & 0x7f) << shift) + pos += 1 + if not (b & 0x80): + result &= mask + result = result_type(result) + return (result, pos) + shift += 7 + if shift >= 64: + raise _DecodeError('Too many bytes when decoding varint.') + return DecodeVarint + + +def _SignedVarintDecoder(bits, result_type): + """Like _VarintDecoder() but decodes signed values.""" + + signbit = 1 << (bits - 1) + mask = (1 << bits) - 1 + + def DecodeVarint(buffer, pos): + result = 0 + shift = 0 + while 1: + b = buffer[pos] + result |= ((b & 0x7f) << shift) + pos += 1 + if not (b & 0x80): + result &= mask + result = (result ^ signbit) - signbit + result = result_type(result) + return (result, pos) + shift += 7 + if shift >= 64: + raise _DecodeError('Too many bytes when decoding varint.') + return DecodeVarint + +# All 32-bit and 64-bit values are represented as int. +_DecodeVarint = _VarintDecoder((1 << 64) - 1, int) +_DecodeSignedVarint = _SignedVarintDecoder(64, int) + +# Use these versions for values which must be limited to 32 bits. +_DecodeVarint32 = _VarintDecoder((1 << 32) - 1, int) +_DecodeSignedVarint32 = _SignedVarintDecoder(32, int) + + +def ReadTag(buffer, pos): + """Read a tag from the memoryview, and return a (tag_bytes, new_pos) tuple. + + We return the raw bytes of the tag rather than decoding them. The raw + bytes can then be used to look up the proper decoder. This effectively allows + us to trade some work that would be done in pure-python (decoding a varint) + for work that is done in C (searching for a byte string in a hash table). + In a low-level language it would be much cheaper to decode the varint and + use that, but not in Python. + + Args: + buffer: memoryview object of the encoded bytes + pos: int of the current position to start from + + Returns: + Tuple[bytes, int] of the tag data and new position. + """ + start = pos + while buffer[pos] & 0x80: + pos += 1 + pos += 1 + + tag_bytes = buffer[start:pos].tobytes() + return tag_bytes, pos + + +# -------------------------------------------------------------------- + + +def _SimpleDecoder(wire_type, decode_value): + """Return a constructor for a decoder for fields of a particular type. + + Args: + wire_type: The field's wire type. + decode_value: A function which decodes an individual value, e.g. + _DecodeVarint() + """ + + def SpecificDecoder(field_number, is_repeated, is_packed, key, new_default, + clear_if_default=False): + if is_packed: + local_DecodeVarint = _DecodeVarint + def DecodePackedField(buffer, pos, end, message, field_dict): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + (endpoint, pos) = local_DecodeVarint(buffer, pos) + endpoint += pos + if endpoint > end: + raise _DecodeError('Truncated message.') + while pos < endpoint: + (element, pos) = decode_value(buffer, pos) + value.append(element) + if pos > endpoint: + del value[-1] # Discard corrupt value. + raise _DecodeError('Packed element was truncated.') + return pos + return DecodePackedField + elif is_repeated: + tag_bytes = encoder.TagBytes(field_number, wire_type) + tag_len = len(tag_bytes) + def DecodeRepeatedField(buffer, pos, end, message, field_dict): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + (element, new_pos) = decode_value(buffer, pos) + value.append(element) + # Predict that the next tag is another copy of the same repeated + # field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos >= end: + # Prediction failed. Return. + if new_pos > end: + raise _DecodeError('Truncated message.') + return new_pos + return DecodeRepeatedField + else: + def DecodeField(buffer, pos, end, message, field_dict): + (new_value, pos) = decode_value(buffer, pos) + if pos > end: + raise _DecodeError('Truncated message.') + if clear_if_default and not new_value: + field_dict.pop(key, None) + else: + field_dict[key] = new_value + return pos + return DecodeField + + return SpecificDecoder + + +def _ModifiedDecoder(wire_type, decode_value, modify_value): + """Like SimpleDecoder but additionally invokes modify_value on every value + before storing it. Usually modify_value is ZigZagDecode. + """ + + # Reusing _SimpleDecoder is slightly slower than copying a bunch of code, but + # not enough to make a significant difference. + + def InnerDecode(buffer, pos): + (result, new_pos) = decode_value(buffer, pos) + return (modify_value(result), new_pos) + return _SimpleDecoder(wire_type, InnerDecode) + + +def _StructPackDecoder(wire_type, format): + """Return a constructor for a decoder for a fixed-width field. + + Args: + wire_type: The field's wire type. + format: The format string to pass to struct.unpack(). + """ + + value_size = struct.calcsize(format) + local_unpack = struct.unpack + + # Reusing _SimpleDecoder is slightly slower than copying a bunch of code, but + # not enough to make a significant difference. + + # Note that we expect someone up-stack to catch struct.error and convert + # it to _DecodeError -- this way we don't have to set up exception- + # handling blocks every time we parse one value. + + def InnerDecode(buffer, pos): + new_pos = pos + value_size + result = local_unpack(format, buffer[pos:new_pos])[0] + return (result, new_pos) + return _SimpleDecoder(wire_type, InnerDecode) + + +def _FloatDecoder(): + """Returns a decoder for a float field. + + This code works around a bug in struct.unpack for non-finite 32-bit + floating-point values. + """ + + local_unpack = struct.unpack + + def InnerDecode(buffer, pos): + """Decode serialized float to a float and new position. + + Args: + buffer: memoryview of the serialized bytes + pos: int, position in the memory view to start at. + + Returns: + Tuple[float, int] of the deserialized float value and new position + in the serialized data. + """ + # We expect a 32-bit value in little-endian byte order. Bit 1 is the sign + # bit, bits 2-9 represent the exponent, and bits 10-32 are the significand. + new_pos = pos + 4 + float_bytes = buffer[pos:new_pos].tobytes() + + # If this value has all its exponent bits set, then it's non-finite. + # In Python 2.4, struct.unpack will convert it to a finite 64-bit value. + # To avoid that, we parse it specially. + if (float_bytes[3:4] in b'\x7F\xFF' and float_bytes[2:3] >= b'\x80'): + # If at least one significand bit is set... + if float_bytes[0:3] != b'\x00\x00\x80': + return (math.nan, new_pos) + # If sign bit is set... + if float_bytes[3:4] == b'\xFF': + return (-math.inf, new_pos) + return (math.inf, new_pos) + + # Note that we expect someone up-stack to catch struct.error and convert + # it to _DecodeError -- this way we don't have to set up exception- + # handling blocks every time we parse one value. + result = local_unpack('= b'\xF0') + and (double_bytes[0:7] != b'\x00\x00\x00\x00\x00\x00\xF0')): + return (math.nan, new_pos) + + # Note that we expect someone up-stack to catch struct.error and convert + # it to _DecodeError -- this way we don't have to set up exception- + # handling blocks every time we parse one value. + result = local_unpack(' end: + raise _DecodeError('Truncated message.') + while pos < endpoint: + value_start_pos = pos + (element, pos) = _DecodeSignedVarint32(buffer, pos) + # pylint: disable=protected-access + if element in enum_type.values_by_number: + value.append(element) + else: + if not message._unknown_fields: + message._unknown_fields = [] + tag_bytes = encoder.TagBytes(field_number, + wire_format.WIRETYPE_VARINT) + + message._unknown_fields.append( + (tag_bytes, buffer[value_start_pos:pos].tobytes())) + if message._unknown_field_set is None: + message._unknown_field_set = containers.UnknownFieldSet() + message._unknown_field_set._add( + field_number, wire_format.WIRETYPE_VARINT, element) + # pylint: enable=protected-access + if pos > endpoint: + if element in enum_type.values_by_number: + del value[-1] # Discard corrupt value. + else: + del message._unknown_fields[-1] + # pylint: disable=protected-access + del message._unknown_field_set._values[-1] + # pylint: enable=protected-access + raise _DecodeError('Packed element was truncated.') + return pos + return DecodePackedField + elif is_repeated: + tag_bytes = encoder.TagBytes(field_number, wire_format.WIRETYPE_VARINT) + tag_len = len(tag_bytes) + def DecodeRepeatedField(buffer, pos, end, message, field_dict): + """Decode serialized repeated enum to its value and a new position. + + Args: + buffer: memoryview of the serialized bytes. + pos: int, position in the memory view to start at. + end: int, end position of serialized data + message: Message object to store unknown fields in + field_dict: Map[Descriptor, Any] to store decoded values in. + + Returns: + int, new position in serialized data. + """ + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + (element, new_pos) = _DecodeSignedVarint32(buffer, pos) + # pylint: disable=protected-access + if element in enum_type.values_by_number: + value.append(element) + else: + if not message._unknown_fields: + message._unknown_fields = [] + message._unknown_fields.append( + (tag_bytes, buffer[pos:new_pos].tobytes())) + if message._unknown_field_set is None: + message._unknown_field_set = containers.UnknownFieldSet() + message._unknown_field_set._add( + field_number, wire_format.WIRETYPE_VARINT, element) + # pylint: enable=protected-access + # Predict that the next tag is another copy of the same repeated + # field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos >= end: + # Prediction failed. Return. + if new_pos > end: + raise _DecodeError('Truncated message.') + return new_pos + return DecodeRepeatedField + else: + def DecodeField(buffer, pos, end, message, field_dict): + """Decode serialized repeated enum to its value and a new position. + + Args: + buffer: memoryview of the serialized bytes. + pos: int, position in the memory view to start at. + end: int, end position of serialized data + message: Message object to store unknown fields in + field_dict: Map[Descriptor, Any] to store decoded values in. + + Returns: + int, new position in serialized data. + """ + value_start_pos = pos + (enum_value, pos) = _DecodeSignedVarint32(buffer, pos) + if pos > end: + raise _DecodeError('Truncated message.') + if clear_if_default and not enum_value: + field_dict.pop(key, None) + return pos + # pylint: disable=protected-access + if enum_value in enum_type.values_by_number: + field_dict[key] = enum_value + else: + if not message._unknown_fields: + message._unknown_fields = [] + tag_bytes = encoder.TagBytes(field_number, + wire_format.WIRETYPE_VARINT) + message._unknown_fields.append( + (tag_bytes, buffer[value_start_pos:pos].tobytes())) + if message._unknown_field_set is None: + message._unknown_field_set = containers.UnknownFieldSet() + message._unknown_field_set._add( + field_number, wire_format.WIRETYPE_VARINT, enum_value) + # pylint: enable=protected-access + return pos + return DecodeField + + +# -------------------------------------------------------------------- + + +Int32Decoder = _SimpleDecoder( + wire_format.WIRETYPE_VARINT, _DecodeSignedVarint32) + +Int64Decoder = _SimpleDecoder( + wire_format.WIRETYPE_VARINT, _DecodeSignedVarint) + +UInt32Decoder = _SimpleDecoder(wire_format.WIRETYPE_VARINT, _DecodeVarint32) +UInt64Decoder = _SimpleDecoder(wire_format.WIRETYPE_VARINT, _DecodeVarint) + +SInt32Decoder = _ModifiedDecoder( + wire_format.WIRETYPE_VARINT, _DecodeVarint32, wire_format.ZigZagDecode) +SInt64Decoder = _ModifiedDecoder( + wire_format.WIRETYPE_VARINT, _DecodeVarint, wire_format.ZigZagDecode) + +# Note that Python conveniently guarantees that when using the '<' prefix on +# formats, they will also have the same size across all platforms (as opposed +# to without the prefix, where their sizes depend on the C compiler's basic +# type sizes). +Fixed32Decoder = _StructPackDecoder(wire_format.WIRETYPE_FIXED32, ' end: + raise _DecodeError('Truncated string.') + value.append(_ConvertToUnicode(buffer[pos:new_pos])) + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + return DecodeRepeatedField + else: + def DecodeField(buffer, pos, end, message, field_dict): + (size, pos) = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated string.') + if clear_if_default and not size: + field_dict.pop(key, None) + else: + field_dict[key] = _ConvertToUnicode(buffer[pos:new_pos]) + return new_pos + return DecodeField + + +def BytesDecoder(field_number, is_repeated, is_packed, key, new_default, + clear_if_default=False): + """Returns a decoder for a bytes field.""" + + local_DecodeVarint = _DecodeVarint + + assert not is_packed + if is_repeated: + tag_bytes = encoder.TagBytes(field_number, + wire_format.WIRETYPE_LENGTH_DELIMITED) + tag_len = len(tag_bytes) + def DecodeRepeatedField(buffer, pos, end, message, field_dict): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + (size, pos) = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated string.') + value.append(buffer[pos:new_pos].tobytes()) + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + return DecodeRepeatedField + else: + def DecodeField(buffer, pos, end, message, field_dict): + (size, pos) = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated string.') + if clear_if_default and not size: + field_dict.pop(key, None) + else: + field_dict[key] = buffer[pos:new_pos].tobytes() + return new_pos + return DecodeField + + +def GroupDecoder(field_number, is_repeated, is_packed, key, new_default): + """Returns a decoder for a group field.""" + + end_tag_bytes = encoder.TagBytes(field_number, + wire_format.WIRETYPE_END_GROUP) + end_tag_len = len(end_tag_bytes) + + assert not is_packed + if is_repeated: + tag_bytes = encoder.TagBytes(field_number, + wire_format.WIRETYPE_START_GROUP) + tag_len = len(tag_bytes) + def DecodeRepeatedField(buffer, pos, end, message, field_dict): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + # Read sub-message. + pos = value.add()._InternalParse(buffer, pos, end) + # Read end tag. + new_pos = pos+end_tag_len + if buffer[pos:new_pos] != end_tag_bytes or new_pos > end: + raise _DecodeError('Missing group end tag.') + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + return DecodeRepeatedField + else: + def DecodeField(buffer, pos, end, message, field_dict): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + # Read sub-message. + pos = value._InternalParse(buffer, pos, end) + # Read end tag. + new_pos = pos+end_tag_len + if buffer[pos:new_pos] != end_tag_bytes or new_pos > end: + raise _DecodeError('Missing group end tag.') + return new_pos + return DecodeField + + +def MessageDecoder(field_number, is_repeated, is_packed, key, new_default): + """Returns a decoder for a message field.""" + + local_DecodeVarint = _DecodeVarint + + assert not is_packed + if is_repeated: + tag_bytes = encoder.TagBytes(field_number, + wire_format.WIRETYPE_LENGTH_DELIMITED) + tag_len = len(tag_bytes) + def DecodeRepeatedField(buffer, pos, end, message, field_dict): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + # Read length. + (size, pos) = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated message.') + # Read sub-message. + if value.add()._InternalParse(buffer, pos, new_pos) != new_pos: + # The only reason _InternalParse would return early is if it + # encountered an end-group tag. + raise _DecodeError('Unexpected end-group tag.') + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + return DecodeRepeatedField + else: + def DecodeField(buffer, pos, end, message, field_dict): + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + # Read length. + (size, pos) = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated message.') + # Read sub-message. + if value._InternalParse(buffer, pos, new_pos) != new_pos: + # The only reason _InternalParse would return early is if it encountered + # an end-group tag. + raise _DecodeError('Unexpected end-group tag.') + return new_pos + return DecodeField + + +# -------------------------------------------------------------------- + +MESSAGE_SET_ITEM_TAG = encoder.TagBytes(1, wire_format.WIRETYPE_START_GROUP) + +def MessageSetItemDecoder(descriptor): + """Returns a decoder for a MessageSet item. + + The parameter is the message Descriptor. + + The message set message looks like this: + message MessageSet { + repeated group Item = 1 { + required int32 type_id = 2; + required string message = 3; + } + } + """ + + type_id_tag_bytes = encoder.TagBytes(2, wire_format.WIRETYPE_VARINT) + message_tag_bytes = encoder.TagBytes(3, wire_format.WIRETYPE_LENGTH_DELIMITED) + item_end_tag_bytes = encoder.TagBytes(1, wire_format.WIRETYPE_END_GROUP) + + local_ReadTag = ReadTag + local_DecodeVarint = _DecodeVarint + local_SkipField = SkipField + + def DecodeItem(buffer, pos, end, message, field_dict): + """Decode serialized message set to its value and new position. + + Args: + buffer: memoryview of the serialized bytes. + pos: int, position in the memory view to start at. + end: int, end position of serialized data + message: Message object to store unknown fields in + field_dict: Map[Descriptor, Any] to store decoded values in. + + Returns: + int, new position in serialized data. + """ + message_set_item_start = pos + type_id = -1 + message_start = -1 + message_end = -1 + + # Technically, type_id and message can appear in any order, so we need + # a little loop here. + while 1: + (tag_bytes, pos) = local_ReadTag(buffer, pos) + if tag_bytes == type_id_tag_bytes: + (type_id, pos) = local_DecodeVarint(buffer, pos) + elif tag_bytes == message_tag_bytes: + (size, message_start) = local_DecodeVarint(buffer, pos) + pos = message_end = message_start + size + elif tag_bytes == item_end_tag_bytes: + break + else: + pos = SkipField(buffer, pos, end, tag_bytes) + if pos == -1: + raise _DecodeError('Missing group end tag.') + + if pos > end: + raise _DecodeError('Truncated message.') + + if type_id == -1: + raise _DecodeError('MessageSet item missing type_id.') + if message_start == -1: + raise _DecodeError('MessageSet item missing message.') + + extension = message.Extensions._FindExtensionByNumber(type_id) + # pylint: disable=protected-access + if extension is not None: + value = field_dict.get(extension) + if value is None: + message_type = extension.message_type + if not hasattr(message_type, '_concrete_class'): + # pylint: disable=protected-access + message._FACTORY.GetPrototype(message_type) + value = field_dict.setdefault( + extension, message_type._concrete_class()) + if value._InternalParse(buffer, message_start,message_end) != message_end: + # The only reason _InternalParse would return early is if it encountered + # an end-group tag. + raise _DecodeError('Unexpected end-group tag.') + else: + if not message._unknown_fields: + message._unknown_fields = [] + message._unknown_fields.append( + (MESSAGE_SET_ITEM_TAG, buffer[message_set_item_start:pos].tobytes())) + if message._unknown_field_set is None: + message._unknown_field_set = containers.UnknownFieldSet() + message._unknown_field_set._add( + type_id, + wire_format.WIRETYPE_LENGTH_DELIMITED, + buffer[message_start:message_end].tobytes()) + # pylint: enable=protected-access + + return pos + + return DecodeItem + +# -------------------------------------------------------------------- + +def MapDecoder(field_descriptor, new_default, is_message_map): + """Returns a decoder for a map field.""" + + key = field_descriptor + tag_bytes = encoder.TagBytes(field_descriptor.number, + wire_format.WIRETYPE_LENGTH_DELIMITED) + tag_len = len(tag_bytes) + local_DecodeVarint = _DecodeVarint + # Can't read _concrete_class yet; might not be initialized. + message_type = field_descriptor.message_type + + def DecodeMap(buffer, pos, end, message, field_dict): + submsg = message_type._concrete_class() + value = field_dict.get(key) + if value is None: + value = field_dict.setdefault(key, new_default(message)) + while 1: + # Read length. + (size, pos) = local_DecodeVarint(buffer, pos) + new_pos = pos + size + if new_pos > end: + raise _DecodeError('Truncated message.') + # Read sub-message. + submsg.Clear() + if submsg._InternalParse(buffer, pos, new_pos) != new_pos: + # The only reason _InternalParse would return early is if it + # encountered an end-group tag. + raise _DecodeError('Unexpected end-group tag.') + + if is_message_map: + value[submsg.key].CopyFrom(submsg.value) + else: + value[submsg.key] = submsg.value + + # Predict that the next tag is another copy of the same repeated field. + pos = new_pos + tag_len + if buffer[new_pos:pos] != tag_bytes or new_pos == end: + # Prediction failed. Return. + return new_pos + + return DecodeMap + +# -------------------------------------------------------------------- +# Optimization is not as heavy here because calls to SkipField() are rare, +# except for handling end-group tags. + +def _SkipVarint(buffer, pos, end): + """Skip a varint value. Returns the new position.""" + # Previously ord(buffer[pos]) raised IndexError when pos is out of range. + # With this code, ord(b'') raises TypeError. Both are handled in + # python_message.py to generate a 'Truncated message' error. + while ord(buffer[pos:pos+1].tobytes()) & 0x80: + pos += 1 + pos += 1 + if pos > end: + raise _DecodeError('Truncated message.') + return pos + +def _SkipFixed64(buffer, pos, end): + """Skip a fixed64 value. Returns the new position.""" + + pos += 8 + if pos > end: + raise _DecodeError('Truncated message.') + return pos + + +def _DecodeFixed64(buffer, pos): + """Decode a fixed64.""" + new_pos = pos + 8 + return (struct.unpack(' end: + raise _DecodeError('Truncated message.') + return pos + + +def _SkipGroup(buffer, pos, end): + """Skip sub-group. Returns the new position.""" + + while 1: + (tag_bytes, pos) = ReadTag(buffer, pos) + new_pos = SkipField(buffer, pos, end, tag_bytes) + if new_pos == -1: + return pos + pos = new_pos + + +def _DecodeUnknownFieldSet(buffer, pos, end_pos=None): + """Decode UnknownFieldSet. Returns the UnknownFieldSet and new position.""" + + unknown_field_set = containers.UnknownFieldSet() + while end_pos is None or pos < end_pos: + (tag_bytes, pos) = ReadTag(buffer, pos) + (tag, _) = _DecodeVarint(tag_bytes, 0) + field_number, wire_type = wire_format.UnpackTag(tag) + if wire_type == wire_format.WIRETYPE_END_GROUP: + break + (data, pos) = _DecodeUnknownField(buffer, pos, wire_type) + # pylint: disable=protected-access + unknown_field_set._add(field_number, wire_type, data) + + return (unknown_field_set, pos) + + +def _DecodeUnknownField(buffer, pos, wire_type): + """Decode a unknown field. Returns the UnknownField and new position.""" + + if wire_type == wire_format.WIRETYPE_VARINT: + (data, pos) = _DecodeVarint(buffer, pos) + elif wire_type == wire_format.WIRETYPE_FIXED64: + (data, pos) = _DecodeFixed64(buffer, pos) + elif wire_type == wire_format.WIRETYPE_FIXED32: + (data, pos) = _DecodeFixed32(buffer, pos) + elif wire_type == wire_format.WIRETYPE_LENGTH_DELIMITED: + (size, pos) = _DecodeVarint(buffer, pos) + data = buffer[pos:pos+size].tobytes() + pos += size + elif wire_type == wire_format.WIRETYPE_START_GROUP: + (data, pos) = _DecodeUnknownFieldSet(buffer, pos) + elif wire_type == wire_format.WIRETYPE_END_GROUP: + return (0, -1) + else: + raise _DecodeError('Wrong wire type in tag.') + + return (data, pos) + + +def _EndGroup(buffer, pos, end): + """Skipping an END_GROUP tag returns -1 to tell the parent loop to break.""" + + return -1 + + +def _SkipFixed32(buffer, pos, end): + """Skip a fixed32 value. Returns the new position.""" + + pos += 4 + if pos > end: + raise _DecodeError('Truncated message.') + return pos + + +def _DecodeFixed32(buffer, pos): + """Decode a fixed32.""" + + new_pos = pos + 4 + return (struct.unpack('B').pack + + def EncodeVarint(write, value, unused_deterministic=None): + bits = value & 0x7f + value >>= 7 + while value: + write(local_int2byte(0x80|bits)) + bits = value & 0x7f + value >>= 7 + return write(local_int2byte(bits)) + + return EncodeVarint + + +def _SignedVarintEncoder(): + """Return an encoder for a basic signed varint value (does not include + tag).""" + + local_int2byte = struct.Struct('>B').pack + + def EncodeSignedVarint(write, value, unused_deterministic=None): + if value < 0: + value += (1 << 64) + bits = value & 0x7f + value >>= 7 + while value: + write(local_int2byte(0x80|bits)) + bits = value & 0x7f + value >>= 7 + return write(local_int2byte(bits)) + + return EncodeSignedVarint + + +_EncodeVarint = _VarintEncoder() +_EncodeSignedVarint = _SignedVarintEncoder() + + +def _VarintBytes(value): + """Encode the given integer as a varint and return the bytes. This is only + called at startup time so it doesn't need to be fast.""" + + pieces = [] + _EncodeVarint(pieces.append, value, True) + return b"".join(pieces) + + +def TagBytes(field_number, wire_type): + """Encode the given tag and return the bytes. Only called at startup.""" + + return bytes(_VarintBytes(wire_format.PackTag(field_number, wire_type))) + +# -------------------------------------------------------------------- +# As with sizers (see above), we have a number of common encoder +# implementations. + + +def _SimpleEncoder(wire_type, encode_value, compute_value_size): + """Return a constructor for an encoder for fields of a particular type. + + Args: + wire_type: The field's wire type, for encoding tags. + encode_value: A function which encodes an individual value, e.g. + _EncodeVarint(). + compute_value_size: A function which computes the size of an individual + value, e.g. _VarintSize(). + """ + + def SpecificEncoder(field_number, is_repeated, is_packed): + if is_packed: + tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + local_EncodeVarint = _EncodeVarint + def EncodePackedField(write, value, deterministic): + write(tag_bytes) + size = 0 + for element in value: + size += compute_value_size(element) + local_EncodeVarint(write, size, deterministic) + for element in value: + encode_value(write, element, deterministic) + return EncodePackedField + elif is_repeated: + tag_bytes = TagBytes(field_number, wire_type) + def EncodeRepeatedField(write, value, deterministic): + for element in value: + write(tag_bytes) + encode_value(write, element, deterministic) + return EncodeRepeatedField + else: + tag_bytes = TagBytes(field_number, wire_type) + def EncodeField(write, value, deterministic): + write(tag_bytes) + return encode_value(write, value, deterministic) + return EncodeField + + return SpecificEncoder + + +def _ModifiedEncoder(wire_type, encode_value, compute_value_size, modify_value): + """Like SimpleEncoder but additionally invokes modify_value on every value + before passing it to encode_value. Usually modify_value is ZigZagEncode.""" + + def SpecificEncoder(field_number, is_repeated, is_packed): + if is_packed: + tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + local_EncodeVarint = _EncodeVarint + def EncodePackedField(write, value, deterministic): + write(tag_bytes) + size = 0 + for element in value: + size += compute_value_size(modify_value(element)) + local_EncodeVarint(write, size, deterministic) + for element in value: + encode_value(write, modify_value(element), deterministic) + return EncodePackedField + elif is_repeated: + tag_bytes = TagBytes(field_number, wire_type) + def EncodeRepeatedField(write, value, deterministic): + for element in value: + write(tag_bytes) + encode_value(write, modify_value(element), deterministic) + return EncodeRepeatedField + else: + tag_bytes = TagBytes(field_number, wire_type) + def EncodeField(write, value, deterministic): + write(tag_bytes) + return encode_value(write, modify_value(value), deterministic) + return EncodeField + + return SpecificEncoder + + +def _StructPackEncoder(wire_type, format): + """Return a constructor for an encoder for a fixed-width field. + + Args: + wire_type: The field's wire type, for encoding tags. + format: The format string to pass to struct.pack(). + """ + + value_size = struct.calcsize(format) + + def SpecificEncoder(field_number, is_repeated, is_packed): + local_struct_pack = struct.pack + if is_packed: + tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + local_EncodeVarint = _EncodeVarint + def EncodePackedField(write, value, deterministic): + write(tag_bytes) + local_EncodeVarint(write, len(value) * value_size, deterministic) + for element in value: + write(local_struct_pack(format, element)) + return EncodePackedField + elif is_repeated: + tag_bytes = TagBytes(field_number, wire_type) + def EncodeRepeatedField(write, value, unused_deterministic=None): + for element in value: + write(tag_bytes) + write(local_struct_pack(format, element)) + return EncodeRepeatedField + else: + tag_bytes = TagBytes(field_number, wire_type) + def EncodeField(write, value, unused_deterministic=None): + write(tag_bytes) + return write(local_struct_pack(format, value)) + return EncodeField + + return SpecificEncoder + + +def _FloatingPointEncoder(wire_type, format): + """Return a constructor for an encoder for float fields. + + This is like StructPackEncoder, but catches errors that may be due to + passing non-finite floating-point values to struct.pack, and makes a + second attempt to encode those values. + + Args: + wire_type: The field's wire type, for encoding tags. + format: The format string to pass to struct.pack(). + """ + + value_size = struct.calcsize(format) + if value_size == 4: + def EncodeNonFiniteOrRaise(write, value): + # Remember that the serialized form uses little-endian byte order. + if value == _POS_INF: + write(b'\x00\x00\x80\x7F') + elif value == _NEG_INF: + write(b'\x00\x00\x80\xFF') + elif value != value: # NaN + write(b'\x00\x00\xC0\x7F') + else: + raise + elif value_size == 8: + def EncodeNonFiniteOrRaise(write, value): + if value == _POS_INF: + write(b'\x00\x00\x00\x00\x00\x00\xF0\x7F') + elif value == _NEG_INF: + write(b'\x00\x00\x00\x00\x00\x00\xF0\xFF') + elif value != value: # NaN + write(b'\x00\x00\x00\x00\x00\x00\xF8\x7F') + else: + raise + else: + raise ValueError('Can\'t encode floating-point values that are ' + '%d bytes long (only 4 or 8)' % value_size) + + def SpecificEncoder(field_number, is_repeated, is_packed): + local_struct_pack = struct.pack + if is_packed: + tag_bytes = TagBytes(field_number, wire_format.WIRETYPE_LENGTH_DELIMITED) + local_EncodeVarint = _EncodeVarint + def EncodePackedField(write, value, deterministic): + write(tag_bytes) + local_EncodeVarint(write, len(value) * value_size, deterministic) + for element in value: + # This try/except block is going to be faster than any code that + # we could write to check whether element is finite. + try: + write(local_struct_pack(format, element)) + except SystemError: + EncodeNonFiniteOrRaise(write, element) + return EncodePackedField + elif is_repeated: + tag_bytes = TagBytes(field_number, wire_type) + def EncodeRepeatedField(write, value, unused_deterministic=None): + for element in value: + write(tag_bytes) + try: + write(local_struct_pack(format, element)) + except SystemError: + EncodeNonFiniteOrRaise(write, element) + return EncodeRepeatedField + else: + tag_bytes = TagBytes(field_number, wire_type) + def EncodeField(write, value, unused_deterministic=None): + write(tag_bytes) + try: + write(local_struct_pack(format, value)) + except SystemError: + EncodeNonFiniteOrRaise(write, value) + return EncodeField + + return SpecificEncoder + + +# ==================================================================== +# Here we declare an encoder constructor for each field type. These work +# very similarly to sizer constructors, described earlier. + + +Int32Encoder = Int64Encoder = EnumEncoder = _SimpleEncoder( + wire_format.WIRETYPE_VARINT, _EncodeSignedVarint, _SignedVarintSize) + +UInt32Encoder = UInt64Encoder = _SimpleEncoder( + wire_format.WIRETYPE_VARINT, _EncodeVarint, _VarintSize) + +SInt32Encoder = SInt64Encoder = _ModifiedEncoder( + wire_format.WIRETYPE_VARINT, _EncodeVarint, _VarintSize, + wire_format.ZigZagEncode) + +# Note that Python conveniently guarantees that when using the '<' prefix on +# formats, they will also have the same size across all platforms (as opposed +# to without the prefix, where their sizes depend on the C compiler's basic +# type sizes). +Fixed32Encoder = _StructPackEncoder(wire_format.WIRETYPE_FIXED32, ' str + ValueType = int + + def __init__(self, enum_type): + """Inits EnumTypeWrapper with an EnumDescriptor.""" + self._enum_type = enum_type + self.DESCRIPTOR = enum_type # pylint: disable=invalid-name + + def Name(self, number): # pylint: disable=invalid-name + """Returns a string containing the name of an enum value.""" + try: + return self._enum_type.values_by_number[number].name + except KeyError: + pass # fall out to break exception chaining + + if not isinstance(number, int): + raise TypeError( + 'Enum value for {} must be an int, but got {} {!r}.'.format( + self._enum_type.name, type(number), number)) + else: + # repr here to handle the odd case when you pass in a boolean. + raise ValueError('Enum {} has no name defined for value {!r}'.format( + self._enum_type.name, number)) + + def Value(self, name): # pylint: disable=invalid-name + """Returns the value corresponding to the given enum name.""" + try: + return self._enum_type.values_by_name[name].number + except KeyError: + pass # fall out to break exception chaining + raise ValueError('Enum {} has no value defined for name {!r}'.format( + self._enum_type.name, name)) + + def keys(self): + """Return a list of the string names in the enum. + + Returns: + A list of strs, in the order they were defined in the .proto file. + """ + + return [value_descriptor.name + for value_descriptor in self._enum_type.values] + + def values(self): + """Return a list of the integer values in the enum. + + Returns: + A list of ints, in the order they were defined in the .proto file. + """ + + return [value_descriptor.number + for value_descriptor in self._enum_type.values] + + def items(self): + """Return a list of the (name, value) pairs of the enum. + + Returns: + A list of (str, int) pairs, in the order they were defined + in the .proto file. + """ + return [(value_descriptor.name, value_descriptor.number) + for value_descriptor in self._enum_type.values] + + def __getattr__(self, name): + """Returns the value corresponding to the given enum name.""" + try: + return super( + EnumTypeWrapper, + self).__getattribute__('_enum_type').values_by_name[name].number + except KeyError: + pass # fall out to break exception chaining + raise AttributeError('Enum {} has no value defined for name {!r}'.format( + self._enum_type.name, name)) diff --git a/lib/protobuf/internal/extension_dict.py b/lib/protobuf/internal/extension_dict.py new file mode 100644 index 0000000..b346cf2 --- /dev/null +++ b/lib/protobuf/internal/extension_dict.py @@ -0,0 +1,213 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Contains _ExtensionDict class to represent extensions. +""" + +from google.protobuf.internal import type_checkers +from google.protobuf.descriptor import FieldDescriptor + + +def _VerifyExtensionHandle(message, extension_handle): + """Verify that the given extension handle is valid.""" + + if not isinstance(extension_handle, FieldDescriptor): + raise KeyError('HasExtension() expects an extension handle, got: %s' % + extension_handle) + + if not extension_handle.is_extension: + raise KeyError('"%s" is not an extension.' % extension_handle.full_name) + + if not extension_handle.containing_type: + raise KeyError('"%s" is missing a containing_type.' + % extension_handle.full_name) + + if extension_handle.containing_type is not message.DESCRIPTOR: + raise KeyError('Extension "%s" extends message type "%s", but this ' + 'message is of type "%s".' % + (extension_handle.full_name, + extension_handle.containing_type.full_name, + message.DESCRIPTOR.full_name)) + + +# TODO(robinson): Unify error handling of "unknown extension" crap. +# TODO(robinson): Support iteritems()-style iteration over all +# extensions with the "has" bits turned on? +class _ExtensionDict(object): + + """Dict-like container for Extension fields on proto instances. + + Note that in all cases we expect extension handles to be + FieldDescriptors. + """ + + def __init__(self, extended_message): + """ + Args: + extended_message: Message instance for which we are the Extensions dict. + """ + self._extended_message = extended_message + + def __getitem__(self, extension_handle): + """Returns the current value of the given extension handle.""" + + _VerifyExtensionHandle(self._extended_message, extension_handle) + + result = self._extended_message._fields.get(extension_handle) + if result is not None: + return result + + if extension_handle.label == FieldDescriptor.LABEL_REPEATED: + result = extension_handle._default_constructor(self._extended_message) + elif extension_handle.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE: + message_type = extension_handle.message_type + if not hasattr(message_type, '_concrete_class'): + # pylint: disable=protected-access + self._extended_message._FACTORY.GetPrototype(message_type) + assert getattr(extension_handle.message_type, '_concrete_class', None), ( + 'Uninitialized concrete class found for field %r (message type %r)' + % (extension_handle.full_name, + extension_handle.message_type.full_name)) + result = extension_handle.message_type._concrete_class() + try: + result._SetListener(self._extended_message._listener_for_children) + except ReferenceError: + pass + else: + # Singular scalar -- just return the default without inserting into the + # dict. + return extension_handle.default_value + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + result = self._extended_message._fields.setdefault( + extension_handle, result) + + return result + + def __eq__(self, other): + if not isinstance(other, self.__class__): + return False + + my_fields = self._extended_message.ListFields() + other_fields = other._extended_message.ListFields() + + # Get rid of non-extension fields. + my_fields = [field for field in my_fields if field.is_extension] + other_fields = [field for field in other_fields if field.is_extension] + + return my_fields == other_fields + + def __ne__(self, other): + return not self == other + + def __len__(self): + fields = self._extended_message.ListFields() + # Get rid of non-extension fields. + extension_fields = [field for field in fields if field[0].is_extension] + return len(extension_fields) + + def __hash__(self): + raise TypeError('unhashable object') + + # Note that this is only meaningful for non-repeated, scalar extension + # fields. Note also that we may have to call _Modified() when we do + # successfully set a field this way, to set any necessary "has" bits in the + # ancestors of the extended message. + def __setitem__(self, extension_handle, value): + """If extension_handle specifies a non-repeated, scalar extension + field, sets the value of that field. + """ + + _VerifyExtensionHandle(self._extended_message, extension_handle) + + if (extension_handle.label == FieldDescriptor.LABEL_REPEATED or + extension_handle.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE): + raise TypeError( + 'Cannot assign to extension "%s" because it is a repeated or ' + 'composite type.' % extension_handle.full_name) + + # It's slightly wasteful to lookup the type checker each time, + # but we expect this to be a vanishingly uncommon case anyway. + type_checker = type_checkers.GetTypeChecker(extension_handle) + # pylint: disable=protected-access + self._extended_message._fields[extension_handle] = ( + type_checker.CheckValue(value)) + self._extended_message._Modified() + + def __delitem__(self, extension_handle): + self._extended_message.ClearExtension(extension_handle) + + def _FindExtensionByName(self, name): + """Tries to find a known extension with the specified name. + + Args: + name: Extension full name. + + Returns: + Extension field descriptor. + """ + return self._extended_message._extensions_by_name.get(name, None) + + def _FindExtensionByNumber(self, number): + """Tries to find a known extension with the field number. + + Args: + number: Extension field number. + + Returns: + Extension field descriptor. + """ + return self._extended_message._extensions_by_number.get(number, None) + + def __iter__(self): + # Return a generator over the populated extension fields + return (f[0] for f in self._extended_message.ListFields() + if f[0].is_extension) + + def __contains__(self, extension_handle): + _VerifyExtensionHandle(self._extended_message, extension_handle) + + if extension_handle not in self._extended_message._fields: + return False + + if extension_handle.label == FieldDescriptor.LABEL_REPEATED: + return bool(self._extended_message._fields.get(extension_handle)) + + if extension_handle.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE: + value = self._extended_message._fields.get(extension_handle) + # pylint: disable=protected-access + return value is not None and value._is_present_in_parent + + return True diff --git a/lib/protobuf/internal/message_listener.py b/lib/protobuf/internal/message_listener.py new file mode 100644 index 0000000..0fc255a --- /dev/null +++ b/lib/protobuf/internal/message_listener.py @@ -0,0 +1,78 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Defines a listener interface for observing certain +state transitions on Message objects. + +Also defines a null implementation of this interface. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + + +class MessageListener(object): + + """Listens for modifications made to a message. Meant to be registered via + Message._SetListener(). + + Attributes: + dirty: If True, then calling Modified() would be a no-op. This can be + used to avoid these calls entirely in the common case. + """ + + def Modified(self): + """Called every time the message is modified in such a way that the parent + message may need to be updated. This currently means either: + (a) The message was modified for the first time, so the parent message + should henceforth mark the message as present. + (b) The message's cached byte size became dirty -- i.e. the message was + modified for the first time after a previous call to ByteSize(). + Therefore the parent should also mark its byte size as dirty. + Note that (a) implies (b), since new objects start out with a client cached + size (zero). However, we document (a) explicitly because it is important. + + Modified() will *only* be called in response to one of these two events -- + not every time the sub-message is modified. + + Note that if the listener's |dirty| attribute is true, then calling + Modified at the moment would be a no-op, so it can be skipped. Performance- + sensitive callers should check this attribute directly before calling since + it will be true most of the time. + """ + + raise NotImplementedError + + +class NullMessageListener(object): + + """No-op MessageListener implementation.""" + + def Modified(self): + pass diff --git a/lib/protobuf/internal/python_message.py b/lib/protobuf/internal/python_message.py new file mode 100644 index 0000000..2921d5c --- /dev/null +++ b/lib/protobuf/internal/python_message.py @@ -0,0 +1,1539 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This code is meant to work on Python 2.4 and above only. +# +# TODO(robinson): Helpers for verbose, common checks like seeing if a +# descriptor's cpp_type is CPPTYPE_MESSAGE. + +"""Contains a metaclass and helper functions used to create +protocol message classes from Descriptor objects at runtime. + +Recall that a metaclass is the "type" of a class. +(A class is to a metaclass what an instance is to a class.) + +In this case, we use the GeneratedProtocolMessageType metaclass +to inject all the useful functionality into the classes +output by the protocol compiler at compile-time. + +The upshot of all this is that the real implementation +details for ALL pure-Python protocol buffers are *here in +this file*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +from io import BytesIO +import struct +import sys +import weakref + +# We use "as" to avoid name collisions with variables. +from google.protobuf.internal import api_implementation +from google.protobuf.internal import containers +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.internal import enum_type_wrapper +from google.protobuf.internal import extension_dict +from google.protobuf.internal import message_listener as message_listener_mod +from google.protobuf.internal import type_checkers +from google.protobuf.internal import well_known_types +from google.protobuf.internal import wire_format +from google.protobuf import descriptor as descriptor_mod +from google.protobuf import message as message_mod +from google.protobuf import text_format + +_FieldDescriptor = descriptor_mod.FieldDescriptor +_AnyFullTypeName = 'google.protobuf.Any' +_ExtensionDict = extension_dict._ExtensionDict + +class GeneratedProtocolMessageType(type): + + """Metaclass for protocol message classes created at runtime from Descriptors. + + We add implementations for all methods described in the Message class. We + also create properties to allow getting/setting all fields in the protocol + message. Finally, we create slots to prevent users from accidentally + "setting" nonexistent fields in the protocol message, which then wouldn't get + serialized / deserialized properly. + + The protocol compiler currently uses this metaclass to create protocol + message classes at runtime. Clients can also manually create their own + classes at runtime, as in this example: + + mydescriptor = Descriptor(.....) + factory = symbol_database.Default() + factory.pool.AddDescriptor(mydescriptor) + MyProtoClass = factory.GetPrototype(mydescriptor) + myproto_instance = MyProtoClass() + myproto.foo_field = 23 + ... + """ + + # Must be consistent with the protocol-compiler code in + # proto2/compiler/internal/generator.*. + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __new__(cls, name, bases, dictionary): + """Custom allocation for runtime-generated class types. + + We override __new__ because this is apparently the only place + where we can meaningfully set __slots__ on the class we're creating(?). + (The interplay between metaclasses and slots is not very well-documented). + + Args: + name: Name of the class (ignored, but required by the + metaclass protocol). + bases: Base classes of the class we're constructing. + (Should be message.Message). We ignore this field, but + it's required by the metaclass protocol + dictionary: The class dictionary of the class we're + constructing. dictionary[_DESCRIPTOR_KEY] must contain + a Descriptor object describing this protocol message + type. + + Returns: + Newly-allocated class. + + Raises: + RuntimeError: Generated code only work with python cpp extension. + """ + descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] + + if isinstance(descriptor, str): + raise RuntimeError('The generated code only work with python cpp ' + 'extension, but it is using pure python runtime.') + + # If a concrete class already exists for this descriptor, don't try to + # create another. Doing so will break any messages that already exist with + # the existing class. + # + # The C++ implementation appears to have its own internal `PyMessageFactory` + # to achieve similar results. + # + # This most commonly happens in `text_format.py` when using descriptors from + # a custom pool; it calls symbol_database.Global().getPrototype() on a + # descriptor which already has an existing concrete class. + new_class = getattr(descriptor, '_concrete_class', None) + if new_class: + return new_class + + if descriptor.full_name in well_known_types.WKTBASES: + bases += (well_known_types.WKTBASES[descriptor.full_name],) + _AddClassAttributesForNestedExtensions(descriptor, dictionary) + _AddSlots(descriptor, dictionary) + + superclass = super(GeneratedProtocolMessageType, cls) + new_class = superclass.__new__(cls, name, bases, dictionary) + return new_class + + def __init__(cls, name, bases, dictionary): + """Here we perform the majority of our work on the class. + We add enum getters, an __init__ method, implementations + of all Message methods, and properties for all fields + in the protocol type. + + Args: + name: Name of the class (ignored, but required by the + metaclass protocol). + bases: Base classes of the class we're constructing. + (Should be message.Message). We ignore this field, but + it's required by the metaclass protocol + dictionary: The class dictionary of the class we're + constructing. dictionary[_DESCRIPTOR_KEY] must contain + a Descriptor object describing this protocol message + type. + """ + descriptor = dictionary[GeneratedProtocolMessageType._DESCRIPTOR_KEY] + + # If this is an _existing_ class looked up via `_concrete_class` in the + # __new__ method above, then we don't need to re-initialize anything. + existing_class = getattr(descriptor, '_concrete_class', None) + if existing_class: + assert existing_class is cls, ( + 'Duplicate `GeneratedProtocolMessageType` created for descriptor %r' + % (descriptor.full_name)) + return + + cls._decoders_by_tag = {} + if (descriptor.has_options and + descriptor.GetOptions().message_set_wire_format): + cls._decoders_by_tag[decoder.MESSAGE_SET_ITEM_TAG] = ( + decoder.MessageSetItemDecoder(descriptor), None) + + # Attach stuff to each FieldDescriptor for quick lookup later on. + for field in descriptor.fields: + _AttachFieldHelpers(cls, field) + + descriptor._concrete_class = cls # pylint: disable=protected-access + _AddEnumValues(descriptor, cls) + _AddInitMethod(descriptor, cls) + _AddPropertiesForFields(descriptor, cls) + _AddPropertiesForExtensions(descriptor, cls) + _AddStaticMethods(cls) + _AddMessageMethods(descriptor, cls) + _AddPrivateHelperMethods(descriptor, cls) + + superclass = super(GeneratedProtocolMessageType, cls) + superclass.__init__(name, bases, dictionary) + + +# Stateless helpers for GeneratedProtocolMessageType below. +# Outside clients should not access these directly. +# +# I opted not to make any of these methods on the metaclass, to make it more +# clear that I'm not really using any state there and to keep clients from +# thinking that they have direct access to these construction helpers. + + +def _PropertyName(proto_field_name): + """Returns the name of the public property attribute which + clients can use to get and (in some cases) set the value + of a protocol message field. + + Args: + proto_field_name: The protocol message field name, exactly + as it appears (or would appear) in a .proto file. + """ + # TODO(robinson): Escape Python keywords (e.g., yield), and test this support. + # nnorwitz makes my day by writing: + # """ + # FYI. See the keyword module in the stdlib. This could be as simple as: + # + # if keyword.iskeyword(proto_field_name): + # return proto_field_name + "_" + # return proto_field_name + # """ + # Kenton says: The above is a BAD IDEA. People rely on being able to use + # getattr() and setattr() to reflectively manipulate field values. If we + # rename the properties, then every such user has to also make sure to apply + # the same transformation. Note that currently if you name a field "yield", + # you can still access it just fine using getattr/setattr -- it's not even + # that cumbersome to do so. + # TODO(kenton): Remove this method entirely if/when everyone agrees with my + # position. + return proto_field_name + + +def _AddSlots(message_descriptor, dictionary): + """Adds a __slots__ entry to dictionary, containing the names of all valid + attributes for this message type. + + Args: + message_descriptor: A Descriptor instance describing this message type. + dictionary: Class dictionary to which we'll add a '__slots__' entry. + """ + dictionary['__slots__'] = ['_cached_byte_size', + '_cached_byte_size_dirty', + '_fields', + '_unknown_fields', + '_unknown_field_set', + '_is_present_in_parent', + '_listener', + '_listener_for_children', + '__weakref__', + '_oneofs'] + + +def _IsMessageSetExtension(field): + return (field.is_extension and + field.containing_type.has_options and + field.containing_type.GetOptions().message_set_wire_format and + field.type == _FieldDescriptor.TYPE_MESSAGE and + field.label == _FieldDescriptor.LABEL_OPTIONAL) + + +def _IsMapField(field): + return (field.type == _FieldDescriptor.TYPE_MESSAGE and + field.message_type.has_options and + field.message_type.GetOptions().map_entry) + + +def _IsMessageMapField(field): + value_type = field.message_type.fields_by_name['value'] + return value_type.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE + + +def _AttachFieldHelpers(cls, field_descriptor): + is_repeated = (field_descriptor.label == _FieldDescriptor.LABEL_REPEATED) + is_packable = (is_repeated and + wire_format.IsTypePackable(field_descriptor.type)) + is_proto3 = field_descriptor.containing_type.syntax == 'proto3' + if not is_packable: + is_packed = False + elif field_descriptor.containing_type.syntax == 'proto2': + is_packed = (field_descriptor.has_options and + field_descriptor.GetOptions().packed) + else: + has_packed_false = (field_descriptor.has_options and + field_descriptor.GetOptions().HasField('packed') and + field_descriptor.GetOptions().packed == False) + is_packed = not has_packed_false + is_map_entry = _IsMapField(field_descriptor) + + if is_map_entry: + field_encoder = encoder.MapEncoder(field_descriptor) + sizer = encoder.MapSizer(field_descriptor, + _IsMessageMapField(field_descriptor)) + elif _IsMessageSetExtension(field_descriptor): + field_encoder = encoder.MessageSetItemEncoder(field_descriptor.number) + sizer = encoder.MessageSetItemSizer(field_descriptor.number) + else: + field_encoder = type_checkers.TYPE_TO_ENCODER[field_descriptor.type]( + field_descriptor.number, is_repeated, is_packed) + sizer = type_checkers.TYPE_TO_SIZER[field_descriptor.type]( + field_descriptor.number, is_repeated, is_packed) + + field_descriptor._encoder = field_encoder + field_descriptor._sizer = sizer + field_descriptor._default_constructor = _DefaultValueConstructorForField( + field_descriptor) + + def AddDecoder(wiretype, is_packed): + tag_bytes = encoder.TagBytes(field_descriptor.number, wiretype) + decode_type = field_descriptor.type + if (decode_type == _FieldDescriptor.TYPE_ENUM and + type_checkers.SupportsOpenEnums(field_descriptor)): + decode_type = _FieldDescriptor.TYPE_INT32 + + oneof_descriptor = None + clear_if_default = False + if field_descriptor.containing_oneof is not None: + oneof_descriptor = field_descriptor + elif (is_proto3 and not is_repeated and + field_descriptor.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE): + clear_if_default = True + + if is_map_entry: + is_message_map = _IsMessageMapField(field_descriptor) + + field_decoder = decoder.MapDecoder( + field_descriptor, _GetInitializeDefaultForMap(field_descriptor), + is_message_map) + elif decode_type == _FieldDescriptor.TYPE_STRING: + field_decoder = decoder.StringDecoder( + field_descriptor.number, is_repeated, is_packed, + field_descriptor, field_descriptor._default_constructor, + clear_if_default) + elif field_descriptor.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + field_decoder = type_checkers.TYPE_TO_DECODER[decode_type]( + field_descriptor.number, is_repeated, is_packed, + field_descriptor, field_descriptor._default_constructor) + else: + field_decoder = type_checkers.TYPE_TO_DECODER[decode_type]( + field_descriptor.number, is_repeated, is_packed, + # pylint: disable=protected-access + field_descriptor, field_descriptor._default_constructor, + clear_if_default) + + cls._decoders_by_tag[tag_bytes] = (field_decoder, oneof_descriptor) + + AddDecoder(type_checkers.FIELD_TYPE_TO_WIRE_TYPE[field_descriptor.type], + False) + + if is_repeated and wire_format.IsTypePackable(field_descriptor.type): + # To support wire compatibility of adding packed = true, add a decoder for + # packed values regardless of the field's options. + AddDecoder(wire_format.WIRETYPE_LENGTH_DELIMITED, True) + + +def _AddClassAttributesForNestedExtensions(descriptor, dictionary): + extensions = descriptor.extensions_by_name + for extension_name, extension_field in extensions.items(): + assert extension_name not in dictionary + dictionary[extension_name] = extension_field + + +def _AddEnumValues(descriptor, cls): + """Sets class-level attributes for all enum fields defined in this message. + + Also exporting a class-level object that can name enum values. + + Args: + descriptor: Descriptor object for this message type. + cls: Class we're constructing for this message type. + """ + for enum_type in descriptor.enum_types: + setattr(cls, enum_type.name, enum_type_wrapper.EnumTypeWrapper(enum_type)) + for enum_value in enum_type.values: + setattr(cls, enum_value.name, enum_value.number) + + +def _GetInitializeDefaultForMap(field): + if field.label != _FieldDescriptor.LABEL_REPEATED: + raise ValueError('map_entry set on non-repeated field %s' % ( + field.name)) + fields_by_name = field.message_type.fields_by_name + key_checker = type_checkers.GetTypeChecker(fields_by_name['key']) + + value_field = fields_by_name['value'] + if _IsMessageMapField(field): + def MakeMessageMapDefault(message): + return containers.MessageMap( + message._listener_for_children, value_field.message_type, key_checker, + field.message_type) + return MakeMessageMapDefault + else: + value_checker = type_checkers.GetTypeChecker(value_field) + def MakePrimitiveMapDefault(message): + return containers.ScalarMap( + message._listener_for_children, key_checker, value_checker, + field.message_type) + return MakePrimitiveMapDefault + +def _DefaultValueConstructorForField(field): + """Returns a function which returns a default value for a field. + + Args: + field: FieldDescriptor object for this field. + + The returned function has one argument: + message: Message instance containing this field, or a weakref proxy + of same. + + That function in turn returns a default value for this field. The default + value may refer back to |message| via a weak reference. + """ + + if _IsMapField(field): + return _GetInitializeDefaultForMap(field) + + if field.label == _FieldDescriptor.LABEL_REPEATED: + if field.has_default_value and field.default_value != []: + raise ValueError('Repeated field default value not empty list: %s' % ( + field.default_value)) + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + # We can't look at _concrete_class yet since it might not have + # been set. (Depends on order in which we initialize the classes). + message_type = field.message_type + def MakeRepeatedMessageDefault(message): + return containers.RepeatedCompositeFieldContainer( + message._listener_for_children, field.message_type) + return MakeRepeatedMessageDefault + else: + type_checker = type_checkers.GetTypeChecker(field) + def MakeRepeatedScalarDefault(message): + return containers.RepeatedScalarFieldContainer( + message._listener_for_children, type_checker) + return MakeRepeatedScalarDefault + + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + # _concrete_class may not yet be initialized. + message_type = field.message_type + def MakeSubMessageDefault(message): + assert getattr(message_type, '_concrete_class', None), ( + 'Uninitialized concrete class found for field %r (message type %r)' + % (field.full_name, message_type.full_name)) + result = message_type._concrete_class() + result._SetListener( + _OneofListener(message, field) + if field.containing_oneof is not None + else message._listener_for_children) + return result + return MakeSubMessageDefault + + def MakeScalarDefault(message): + # TODO(protobuf-team): This may be broken since there may not be + # default_value. Combine with has_default_value somehow. + return field.default_value + return MakeScalarDefault + + +def _ReraiseTypeErrorWithFieldName(message_name, field_name): + """Re-raise the currently-handled TypeError with the field name added.""" + exc = sys.exc_info()[1] + if len(exc.args) == 1 and type(exc) is TypeError: + # simple TypeError; add field name to exception message + exc = TypeError('%s for field %s.%s' % (str(exc), message_name, field_name)) + + # re-raise possibly-amended exception with original traceback: + raise exc.with_traceback(sys.exc_info()[2]) + + +def _AddInitMethod(message_descriptor, cls): + """Adds an __init__ method to cls.""" + + def _GetIntegerEnumValue(enum_type, value): + """Convert a string or integer enum value to an integer. + + If the value is a string, it is converted to the enum value in + enum_type with the same name. If the value is not a string, it's + returned as-is. (No conversion or bounds-checking is done.) + """ + if isinstance(value, str): + try: + return enum_type.values_by_name[value].number + except KeyError: + raise ValueError('Enum type %s: unknown label "%s"' % ( + enum_type.full_name, value)) + return value + + def init(self, **kwargs): + self._cached_byte_size = 0 + self._cached_byte_size_dirty = len(kwargs) > 0 + self._fields = {} + # Contains a mapping from oneof field descriptors to the descriptor + # of the currently set field in that oneof field. + self._oneofs = {} + + # _unknown_fields is () when empty for efficiency, and will be turned into + # a list if fields are added. + self._unknown_fields = () + # _unknown_field_set is None when empty for efficiency, and will be + # turned into UnknownFieldSet struct if fields are added. + self._unknown_field_set = None # pylint: disable=protected-access + self._is_present_in_parent = False + self._listener = message_listener_mod.NullMessageListener() + self._listener_for_children = _Listener(self) + for field_name, field_value in kwargs.items(): + field = _GetFieldByName(message_descriptor, field_name) + if field is None: + raise TypeError('%s() got an unexpected keyword argument "%s"' % + (message_descriptor.name, field_name)) + if field_value is None: + # field=None is the same as no field at all. + continue + if field.label == _FieldDescriptor.LABEL_REPEATED: + copy = field._default_constructor(self) + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: # Composite + if _IsMapField(field): + if _IsMessageMapField(field): + for key in field_value: + copy[key].MergeFrom(field_value[key]) + else: + copy.update(field_value) + else: + for val in field_value: + if isinstance(val, dict): + copy.add(**val) + else: + copy.add().MergeFrom(val) + else: # Scalar + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + field_value = [_GetIntegerEnumValue(field.enum_type, val) + for val in field_value] + copy.extend(field_value) + self._fields[field] = copy + elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + copy = field._default_constructor(self) + new_val = field_value + if isinstance(field_value, dict): + new_val = field.message_type._concrete_class(**field_value) + try: + copy.MergeFrom(new_val) + except TypeError: + _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name) + self._fields[field] = copy + else: + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + field_value = _GetIntegerEnumValue(field.enum_type, field_value) + try: + setattr(self, field_name, field_value) + except TypeError: + _ReraiseTypeErrorWithFieldName(message_descriptor.name, field_name) + + init.__module__ = None + init.__doc__ = None + cls.__init__ = init + + +def _GetFieldByName(message_descriptor, field_name): + """Returns a field descriptor by field name. + + Args: + message_descriptor: A Descriptor describing all fields in message. + field_name: The name of the field to retrieve. + Returns: + The field descriptor associated with the field name. + """ + try: + return message_descriptor.fields_by_name[field_name] + except KeyError: + raise ValueError('Protocol message %s has no "%s" field.' % + (message_descriptor.name, field_name)) + + +def _AddPropertiesForFields(descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + for field in descriptor.fields: + _AddPropertiesForField(field, cls) + + if descriptor.is_extendable: + # _ExtensionDict is just an adaptor with no state so we allocate a new one + # every time it is accessed. + cls.Extensions = property(lambda self: _ExtensionDict(self)) + + +def _AddPropertiesForField(field, cls): + """Adds a public property for a protocol message field. + Clients can use this property to get and (in the case + of non-repeated scalar fields) directly set the value + of a protocol message field. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # Catch it if we add other types that we should + # handle specially here. + assert _FieldDescriptor.MAX_CPPTYPE == 10 + + constant_name = field.name.upper() + '_FIELD_NUMBER' + setattr(cls, constant_name, field.number) + + if field.label == _FieldDescriptor.LABEL_REPEATED: + _AddPropertiesForRepeatedField(field, cls) + elif field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + _AddPropertiesForNonRepeatedCompositeField(field, cls) + else: + _AddPropertiesForNonRepeatedScalarField(field, cls) + + +class _FieldProperty(property): + __slots__ = ('DESCRIPTOR',) + + def __init__(self, descriptor, getter, setter, doc): + property.__init__(self, getter, setter, doc=doc) + self.DESCRIPTOR = descriptor + + +def _AddPropertiesForRepeatedField(field, cls): + """Adds a public property for a "repeated" protocol message field. Clients + can use this property to get the value of the field, which will be either a + RepeatedScalarFieldContainer or RepeatedCompositeFieldContainer (see + below). + + Note that when clients add values to these containers, we perform + type-checking in the case of repeated scalar fields, and we also set any + necessary "has" bits as a side-effect. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + + def getter(self): + field_value = self._fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + field_value = self._fields.setdefault(field, field_value) + return field_value + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError('Assignment not allowed to repeated field ' + '"%s" in protocol message object.' % proto_field_name) + + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedScalarField(field, cls): + """Adds a public property for a nonrepeated, scalar protocol message field. + Clients can use this property to get and directly set the value of the field. + Note that when the client sets the value of a field by using this property, + all necessary "has" bits are set as a side-effect, and we also perform + type-checking. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + type_checker = type_checkers.GetTypeChecker(field) + default_value = field.default_value + is_proto3 = field.containing_type.syntax == 'proto3' + + def getter(self): + # TODO(protobuf-team): This may be broken since there may not be + # default_value. Combine with has_default_value somehow. + return self._fields.get(field, default_value) + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + clear_when_set_to_default = is_proto3 and not field.containing_oneof + + def field_setter(self, new_value): + # pylint: disable=protected-access + # Testing the value for truthiness captures all of the proto3 defaults + # (0, 0.0, enum 0, and False). + try: + new_value = type_checker.CheckValue(new_value) + except TypeError as e: + raise TypeError( + 'Cannot set %s to %.1024r: %s' % (field.full_name, new_value, e)) + if clear_when_set_to_default and not new_value: + self._fields.pop(field, None) + else: + self._fields[field] = new_value + # Check _cached_byte_size_dirty inline to improve performance, since scalar + # setters are called frequently. + if not self._cached_byte_size_dirty: + self._Modified() + + if field.containing_oneof: + def setter(self, new_value): + field_setter(self, new_value) + self._UpdateOneofState(field) + else: + setter = field_setter + + setter.__module__ = None + setter.__doc__ = 'Setter for %s.' % proto_field_name + + # Add a property to encapsulate the getter/setter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc)) + + +def _AddPropertiesForNonRepeatedCompositeField(field, cls): + """Adds a public property for a nonrepeated, composite protocol message field. + A composite field is a "group" or "message" field. + + Clients can use this property to get the value of the field, but cannot + assign to the property directly. + + Args: + field: A FieldDescriptor for this field. + cls: The class we're constructing. + """ + # TODO(robinson): Remove duplication with similar method + # for non-repeated scalars. + proto_field_name = field.name + property_name = _PropertyName(proto_field_name) + + def getter(self): + field_value = self._fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + + # Atomically check if another thread has preempted us and, if not, swap + # in the new object we just created. If someone has preempted us, we + # take that object and discard ours. + # WARNING: We are relying on setdefault() being atomic. This is true + # in CPython but we haven't investigated others. This warning appears + # in several other locations in this file. + field_value = self._fields.setdefault(field, field_value) + return field_value + getter.__module__ = None + getter.__doc__ = 'Getter for %s.' % proto_field_name + + # We define a setter just so we can throw an exception with a more + # helpful error message. + def setter(self, new_value): + raise AttributeError('Assignment not allowed to composite field ' + '"%s" in protocol message object.' % proto_field_name) + + # Add a property to encapsulate the getter. + doc = 'Magic attribute generated for "%s" proto field.' % proto_field_name + setattr(cls, property_name, _FieldProperty(field, getter, setter, doc=doc)) + + +def _AddPropertiesForExtensions(descriptor, cls): + """Adds properties for all fields in this protocol message type.""" + extensions = descriptor.extensions_by_name + for extension_name, extension_field in extensions.items(): + constant_name = extension_name.upper() + '_FIELD_NUMBER' + setattr(cls, constant_name, extension_field.number) + + # TODO(amauryfa): Migrate all users of these attributes to functions like + # pool.FindExtensionByNumber(descriptor). + if descriptor.file is not None: + # TODO(amauryfa): Use cls.MESSAGE_FACTORY.pool when available. + pool = descriptor.file.pool + cls._extensions_by_number = pool._extensions_by_number[descriptor] + cls._extensions_by_name = pool._extensions_by_name[descriptor] + +def _AddStaticMethods(cls): + # TODO(robinson): This probably needs to be thread-safe(?) + def RegisterExtension(extension_handle): + extension_handle.containing_type = cls.DESCRIPTOR + # TODO(amauryfa): Use cls.MESSAGE_FACTORY.pool when available. + # pylint: disable=protected-access + cls.DESCRIPTOR.file.pool._AddExtensionDescriptor(extension_handle) + _AttachFieldHelpers(cls, extension_handle) + cls.RegisterExtension = staticmethod(RegisterExtension) + + def FromString(s): + message = cls() + message.MergeFromString(s) + return message + cls.FromString = staticmethod(FromString) + + +def _IsPresent(item): + """Given a (FieldDescriptor, value) tuple from _fields, return true if the + value should be included in the list returned by ListFields().""" + + if item[0].label == _FieldDescriptor.LABEL_REPEATED: + return bool(item[1]) + elif item[0].cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + return item[1]._is_present_in_parent + else: + return True + + +def _AddListFieldsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def ListFields(self): + all_fields = [item for item in self._fields.items() if _IsPresent(item)] + all_fields.sort(key = lambda item: item[0].number) + return all_fields + + cls.ListFields = ListFields + +_PROTO3_ERROR_TEMPLATE = \ + ('Protocol message %s has no non-repeated submessage field "%s" ' + 'nor marked as optional') +_PROTO2_ERROR_TEMPLATE = 'Protocol message %s has no non-repeated field "%s"' + +def _AddHasFieldMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + is_proto3 = (message_descriptor.syntax == "proto3") + error_msg = _PROTO3_ERROR_TEMPLATE if is_proto3 else _PROTO2_ERROR_TEMPLATE + + hassable_fields = {} + for field in message_descriptor.fields: + if field.label == _FieldDescriptor.LABEL_REPEATED: + continue + # For proto3, only submessages and fields inside a oneof have presence. + if (is_proto3 and field.cpp_type != _FieldDescriptor.CPPTYPE_MESSAGE and + not field.containing_oneof): + continue + hassable_fields[field.name] = field + + # Has methods are supported for oneof descriptors. + for oneof in message_descriptor.oneofs: + hassable_fields[oneof.name] = oneof + + def HasField(self, field_name): + try: + field = hassable_fields[field_name] + except KeyError: + raise ValueError(error_msg % (message_descriptor.full_name, field_name)) + + if isinstance(field, descriptor_mod.OneofDescriptor): + try: + return HasField(self, self._oneofs[field].name) + except KeyError: + return False + else: + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + value = self._fields.get(field) + return value is not None and value._is_present_in_parent + else: + return field in self._fields + + cls.HasField = HasField + + +def _AddClearFieldMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def ClearField(self, field_name): + try: + field = message_descriptor.fields_by_name[field_name] + except KeyError: + try: + field = message_descriptor.oneofs_by_name[field_name] + if field in self._oneofs: + field = self._oneofs[field] + else: + return + except KeyError: + raise ValueError('Protocol message %s has no "%s" field.' % + (message_descriptor.name, field_name)) + + if field in self._fields: + # To match the C++ implementation, we need to invalidate iterators + # for map fields when ClearField() happens. + if hasattr(self._fields[field], 'InvalidateIterators'): + self._fields[field].InvalidateIterators() + + # Note: If the field is a sub-message, its listener will still point + # at us. That's fine, because the worst than can happen is that it + # will call _Modified() and invalidate our byte size. Big deal. + del self._fields[field] + + if self._oneofs.get(field.containing_oneof, None) is field: + del self._oneofs[field.containing_oneof] + + # Always call _Modified() -- even if nothing was changed, this is + # a mutating method, and thus calling it should cause the field to become + # present in the parent message. + self._Modified() + + cls.ClearField = ClearField + + +def _AddClearExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + def ClearExtension(self, extension_handle): + extension_dict._VerifyExtensionHandle(self, extension_handle) + + # Similar to ClearField(), above. + if extension_handle in self._fields: + del self._fields[extension_handle] + self._Modified() + cls.ClearExtension = ClearExtension + + +def _AddHasExtensionMethod(cls): + """Helper for _AddMessageMethods().""" + def HasExtension(self, extension_handle): + extension_dict._VerifyExtensionHandle(self, extension_handle) + if extension_handle.label == _FieldDescriptor.LABEL_REPEATED: + raise KeyError('"%s" is repeated.' % extension_handle.full_name) + + if extension_handle.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + value = self._fields.get(extension_handle) + return value is not None and value._is_present_in_parent + else: + return extension_handle in self._fields + cls.HasExtension = HasExtension + +def _InternalUnpackAny(msg): + """Unpacks Any message and returns the unpacked message. + + This internal method is different from public Any Unpack method which takes + the target message as argument. _InternalUnpackAny method does not have + target message type and need to find the message type in descriptor pool. + + Args: + msg: An Any message to be unpacked. + + Returns: + The unpacked message. + """ + # TODO(amauryfa): Don't use the factory of generated messages. + # To make Any work with custom factories, use the message factory of the + # parent message. + # pylint: disable=g-import-not-at-top + from google.protobuf import symbol_database + factory = symbol_database.Default() + + type_url = msg.type_url + + if not type_url: + return None + + # TODO(haberman): For now we just strip the hostname. Better logic will be + # required. + type_name = type_url.split('/')[-1] + descriptor = factory.pool.FindMessageTypeByName(type_name) + + if descriptor is None: + return None + + message_class = factory.GetPrototype(descriptor) + message = message_class() + + message.ParseFromString(msg.value) + return message + + +def _AddEqualsMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def __eq__(self, other): + if (not isinstance(other, message_mod.Message) or + other.DESCRIPTOR != self.DESCRIPTOR): + return False + + if self is other: + return True + + if self.DESCRIPTOR.full_name == _AnyFullTypeName: + any_a = _InternalUnpackAny(self) + any_b = _InternalUnpackAny(other) + if any_a and any_b: + return any_a == any_b + + if not self.ListFields() == other.ListFields(): + return False + + # TODO(jieluo): Fix UnknownFieldSet to consider MessageSet extensions, + # then use it for the comparison. + unknown_fields = list(self._unknown_fields) + unknown_fields.sort() + other_unknown_fields = list(other._unknown_fields) + other_unknown_fields.sort() + return unknown_fields == other_unknown_fields + + cls.__eq__ = __eq__ + + +def _AddStrMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def __str__(self): + return text_format.MessageToString(self) + cls.__str__ = __str__ + + +def _AddReprMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def __repr__(self): + return text_format.MessageToString(self) + cls.__repr__ = __repr__ + + +def _AddUnicodeMethod(unused_message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def __unicode__(self): + return text_format.MessageToString(self, as_utf8=True).decode('utf-8') + cls.__unicode__ = __unicode__ + + +def _BytesForNonRepeatedElement(value, field_number, field_type): + """Returns the number of bytes needed to serialize a non-repeated element. + The returned byte count includes space for tag information and any + other additional space associated with serializing value. + + Args: + value: Value we're serializing. + field_number: Field number of this value. (Since the field number + is stored as part of a varint-encoded tag, this has an impact + on the total bytes required to serialize the value). + field_type: The type of the field. One of the TYPE_* constants + within FieldDescriptor. + """ + try: + fn = type_checkers.TYPE_TO_BYTE_SIZE_FN[field_type] + return fn(field_number, value) + except KeyError: + raise message_mod.EncodeError('Unrecognized field type: %d' % field_type) + + +def _AddByteSizeMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def ByteSize(self): + if not self._cached_byte_size_dirty: + return self._cached_byte_size + + size = 0 + descriptor = self.DESCRIPTOR + if descriptor.GetOptions().map_entry: + # Fields of map entry should always be serialized. + size = descriptor.fields_by_name['key']._sizer(self.key) + size += descriptor.fields_by_name['value']._sizer(self.value) + else: + for field_descriptor, field_value in self.ListFields(): + size += field_descriptor._sizer(field_value) + for tag_bytes, value_bytes in self._unknown_fields: + size += len(tag_bytes) + len(value_bytes) + + self._cached_byte_size = size + self._cached_byte_size_dirty = False + self._listener_for_children.dirty = False + return size + + cls.ByteSize = ByteSize + + +def _AddSerializeToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def SerializeToString(self, **kwargs): + # Check if the message has all of its required fields set. + if not self.IsInitialized(): + raise message_mod.EncodeError( + 'Message %s is missing required fields: %s' % ( + self.DESCRIPTOR.full_name, ','.join(self.FindInitializationErrors()))) + return self.SerializePartialToString(**kwargs) + cls.SerializeToString = SerializeToString + + +def _AddSerializePartialToStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + + def SerializePartialToString(self, **kwargs): + out = BytesIO() + self._InternalSerialize(out.write, **kwargs) + return out.getvalue() + cls.SerializePartialToString = SerializePartialToString + + def InternalSerialize(self, write_bytes, deterministic=None): + if deterministic is None: + deterministic = ( + api_implementation.IsPythonDefaultSerializationDeterministic()) + else: + deterministic = bool(deterministic) + + descriptor = self.DESCRIPTOR + if descriptor.GetOptions().map_entry: + # Fields of map entry should always be serialized. + descriptor.fields_by_name['key']._encoder( + write_bytes, self.key, deterministic) + descriptor.fields_by_name['value']._encoder( + write_bytes, self.value, deterministic) + else: + for field_descriptor, field_value in self.ListFields(): + field_descriptor._encoder(write_bytes, field_value, deterministic) + for tag_bytes, value_bytes in self._unknown_fields: + write_bytes(tag_bytes) + write_bytes(value_bytes) + cls._InternalSerialize = InternalSerialize + + +def _AddMergeFromStringMethod(message_descriptor, cls): + """Helper for _AddMessageMethods().""" + def MergeFromString(self, serialized): + serialized = memoryview(serialized) + length = len(serialized) + try: + if self._InternalParse(serialized, 0, length) != length: + # The only reason _InternalParse would return early is if it + # encountered an end-group tag. + raise message_mod.DecodeError('Unexpected end-group tag.') + except (IndexError, TypeError): + # Now ord(buf[p:p+1]) == ord('') gets TypeError. + raise message_mod.DecodeError('Truncated message.') + except struct.error as e: + raise message_mod.DecodeError(e) + return length # Return this for legacy reasons. + cls.MergeFromString = MergeFromString + + local_ReadTag = decoder.ReadTag + local_SkipField = decoder.SkipField + decoders_by_tag = cls._decoders_by_tag + + def InternalParse(self, buffer, pos, end): + """Create a message from serialized bytes. + + Args: + self: Message, instance of the proto message object. + buffer: memoryview of the serialized data. + pos: int, position to start in the serialized data. + end: int, end position of the serialized data. + + Returns: + Message object. + """ + # Guard against internal misuse, since this function is called internally + # quite extensively, and its easy to accidentally pass bytes. + assert isinstance(buffer, memoryview) + self._Modified() + field_dict = self._fields + # pylint: disable=protected-access + unknown_field_set = self._unknown_field_set + while pos != end: + (tag_bytes, new_pos) = local_ReadTag(buffer, pos) + field_decoder, field_desc = decoders_by_tag.get(tag_bytes, (None, None)) + if field_decoder is None: + if not self._unknown_fields: # pylint: disable=protected-access + self._unknown_fields = [] # pylint: disable=protected-access + if unknown_field_set is None: + # pylint: disable=protected-access + self._unknown_field_set = containers.UnknownFieldSet() + # pylint: disable=protected-access + unknown_field_set = self._unknown_field_set + # pylint: disable=protected-access + (tag, _) = decoder._DecodeVarint(tag_bytes, 0) + field_number, wire_type = wire_format.UnpackTag(tag) + if field_number == 0: + raise message_mod.DecodeError('Field number 0 is illegal.') + # TODO(jieluo): remove old_pos. + old_pos = new_pos + (data, new_pos) = decoder._DecodeUnknownField( + buffer, new_pos, wire_type) # pylint: disable=protected-access + if new_pos == -1: + return pos + # pylint: disable=protected-access + unknown_field_set._add(field_number, wire_type, data) + # TODO(jieluo): remove _unknown_fields. + new_pos = local_SkipField(buffer, old_pos, end, tag_bytes) + if new_pos == -1: + return pos + self._unknown_fields.append( + (tag_bytes, buffer[old_pos:new_pos].tobytes())) + pos = new_pos + else: + pos = field_decoder(buffer, new_pos, end, self, field_dict) + if field_desc: + self._UpdateOneofState(field_desc) + return pos + cls._InternalParse = InternalParse + + +def _AddIsInitializedMethod(message_descriptor, cls): + """Adds the IsInitialized and FindInitializationError methods to the + protocol message class.""" + + required_fields = [field for field in message_descriptor.fields + if field.label == _FieldDescriptor.LABEL_REQUIRED] + + def IsInitialized(self, errors=None): + """Checks if all required fields of a message are set. + + Args: + errors: A list which, if provided, will be populated with the field + paths of all missing required fields. + + Returns: + True iff the specified message has all required fields set. + """ + + # Performance is critical so we avoid HasField() and ListFields(). + + for field in required_fields: + if (field not in self._fields or + (field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE and + not self._fields[field]._is_present_in_parent)): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + + for field, value in list(self._fields.items()): # dict can change size! + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if field.label == _FieldDescriptor.LABEL_REPEATED: + if (field.message_type.has_options and + field.message_type.GetOptions().map_entry): + continue + for element in value: + if not element.IsInitialized(): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + elif value._is_present_in_parent and not value.IsInitialized(): + if errors is not None: + errors.extend(self.FindInitializationErrors()) + return False + + return True + + cls.IsInitialized = IsInitialized + + def FindInitializationErrors(self): + """Finds required fields which are not initialized. + + Returns: + A list of strings. Each string is a path to an uninitialized field from + the top-level message, e.g. "foo.bar[5].baz". + """ + + errors = [] # simplify things + + for field in required_fields: + if not self.HasField(field.name): + errors.append(field.name) + + for field, value in self.ListFields(): + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if field.is_extension: + name = '(%s)' % field.full_name + else: + name = field.name + + if _IsMapField(field): + if _IsMessageMapField(field): + for key in value: + element = value[key] + prefix = '%s[%s].' % (name, key) + sub_errors = element.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + else: + # ScalarMaps can't have any initialization errors. + pass + elif field.label == _FieldDescriptor.LABEL_REPEATED: + for i in range(len(value)): + element = value[i] + prefix = '%s[%d].' % (name, i) + sub_errors = element.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + else: + prefix = name + '.' + sub_errors = value.FindInitializationErrors() + errors += [prefix + error for error in sub_errors] + + return errors + + cls.FindInitializationErrors = FindInitializationErrors + + +def _FullyQualifiedClassName(klass): + module = klass.__module__ + name = getattr(klass, '__qualname__', klass.__name__) + if module in (None, 'builtins', '__builtin__'): + return name + return module + '.' + name + + +def _AddMergeFromMethod(cls): + LABEL_REPEATED = _FieldDescriptor.LABEL_REPEATED + CPPTYPE_MESSAGE = _FieldDescriptor.CPPTYPE_MESSAGE + + def MergeFrom(self, msg): + if not isinstance(msg, cls): + raise TypeError( + 'Parameter to MergeFrom() must be instance of same class: ' + 'expected %s got %s.' % (_FullyQualifiedClassName(cls), + _FullyQualifiedClassName(msg.__class__))) + + assert msg is not self + self._Modified() + + fields = self._fields + + for field, value in msg._fields.items(): + if field.label == LABEL_REPEATED: + field_value = fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + fields[field] = field_value + field_value.MergeFrom(value) + elif field.cpp_type == CPPTYPE_MESSAGE: + if value._is_present_in_parent: + field_value = fields.get(field) + if field_value is None: + # Construct a new object to represent this field. + field_value = field._default_constructor(self) + fields[field] = field_value + field_value.MergeFrom(value) + else: + self._fields[field] = value + if field.containing_oneof: + self._UpdateOneofState(field) + + if msg._unknown_fields: + if not self._unknown_fields: + self._unknown_fields = [] + self._unknown_fields.extend(msg._unknown_fields) + # pylint: disable=protected-access + if self._unknown_field_set is None: + self._unknown_field_set = containers.UnknownFieldSet() + self._unknown_field_set._extend(msg._unknown_field_set) + + cls.MergeFrom = MergeFrom + + +def _AddWhichOneofMethod(message_descriptor, cls): + def WhichOneof(self, oneof_name): + """Returns the name of the currently set field inside a oneof, or None.""" + try: + field = message_descriptor.oneofs_by_name[oneof_name] + except KeyError: + raise ValueError( + 'Protocol message has no oneof "%s" field.' % oneof_name) + + nested_field = self._oneofs.get(field, None) + if nested_field is not None and self.HasField(nested_field.name): + return nested_field.name + else: + return None + + cls.WhichOneof = WhichOneof + + +def _Clear(self): + # Clear fields. + self._fields = {} + self._unknown_fields = () + # pylint: disable=protected-access + if self._unknown_field_set is not None: + self._unknown_field_set._clear() + self._unknown_field_set = None + + self._oneofs = {} + self._Modified() + + +def _UnknownFields(self): + if self._unknown_field_set is None: # pylint: disable=protected-access + # pylint: disable=protected-access + self._unknown_field_set = containers.UnknownFieldSet() + return self._unknown_field_set # pylint: disable=protected-access + + +def _DiscardUnknownFields(self): + self._unknown_fields = [] + self._unknown_field_set = None # pylint: disable=protected-access + for field, value in self.ListFields(): + if field.cpp_type == _FieldDescriptor.CPPTYPE_MESSAGE: + if _IsMapField(field): + if _IsMessageMapField(field): + for key in value: + value[key].DiscardUnknownFields() + elif field.label == _FieldDescriptor.LABEL_REPEATED: + for sub_message in value: + sub_message.DiscardUnknownFields() + else: + value.DiscardUnknownFields() + + +def _SetListener(self, listener): + if listener is None: + self._listener = message_listener_mod.NullMessageListener() + else: + self._listener = listener + + +def _AddMessageMethods(message_descriptor, cls): + """Adds implementations of all Message methods to cls.""" + _AddListFieldsMethod(message_descriptor, cls) + _AddHasFieldMethod(message_descriptor, cls) + _AddClearFieldMethod(message_descriptor, cls) + if message_descriptor.is_extendable: + _AddClearExtensionMethod(cls) + _AddHasExtensionMethod(cls) + _AddEqualsMethod(message_descriptor, cls) + _AddStrMethod(message_descriptor, cls) + _AddReprMethod(message_descriptor, cls) + _AddUnicodeMethod(message_descriptor, cls) + _AddByteSizeMethod(message_descriptor, cls) + _AddSerializeToStringMethod(message_descriptor, cls) + _AddSerializePartialToStringMethod(message_descriptor, cls) + _AddMergeFromStringMethod(message_descriptor, cls) + _AddIsInitializedMethod(message_descriptor, cls) + _AddMergeFromMethod(cls) + _AddWhichOneofMethod(message_descriptor, cls) + # Adds methods which do not depend on cls. + cls.Clear = _Clear + cls.UnknownFields = _UnknownFields + cls.DiscardUnknownFields = _DiscardUnknownFields + cls._SetListener = _SetListener + + +def _AddPrivateHelperMethods(message_descriptor, cls): + """Adds implementation of private helper methods to cls.""" + + def Modified(self): + """Sets the _cached_byte_size_dirty bit to true, + and propagates this to our listener iff this was a state change. + """ + + # Note: Some callers check _cached_byte_size_dirty before calling + # _Modified() as an extra optimization. So, if this method is ever + # changed such that it does stuff even when _cached_byte_size_dirty is + # already true, the callers need to be updated. + if not self._cached_byte_size_dirty: + self._cached_byte_size_dirty = True + self._listener_for_children.dirty = True + self._is_present_in_parent = True + self._listener.Modified() + + def _UpdateOneofState(self, field): + """Sets field as the active field in its containing oneof. + + Will also delete currently active field in the oneof, if it is different + from the argument. Does not mark the message as modified. + """ + other_field = self._oneofs.setdefault(field.containing_oneof, field) + if other_field is not field: + del self._fields[other_field] + self._oneofs[field.containing_oneof] = field + + cls._Modified = Modified + cls.SetInParent = Modified + cls._UpdateOneofState = _UpdateOneofState + + +class _Listener(object): + + """MessageListener implementation that a parent message registers with its + child message. + + In order to support semantics like: + + foo.bar.baz.qux = 23 + assert foo.HasField('bar') + + ...child objects must have back references to their parents. + This helper class is at the heart of this support. + """ + + def __init__(self, parent_message): + """Args: + parent_message: The message whose _Modified() method we should call when + we receive Modified() messages. + """ + # This listener establishes a back reference from a child (contained) object + # to its parent (containing) object. We make this a weak reference to avoid + # creating cyclic garbage when the client finishes with the 'parent' object + # in the tree. + if isinstance(parent_message, weakref.ProxyType): + self._parent_message_weakref = parent_message + else: + self._parent_message_weakref = weakref.proxy(parent_message) + + # As an optimization, we also indicate directly on the listener whether + # or not the parent message is dirty. This way we can avoid traversing + # up the tree in the common case. + self.dirty = False + + def Modified(self): + if self.dirty: + return + try: + # Propagate the signal to our parents iff this is the first field set. + self._parent_message_weakref._Modified() + except ReferenceError: + # We can get here if a client has kept a reference to a child object, + # and is now setting a field on it, but the child's parent has been + # garbage-collected. This is not an error. + pass + + +class _OneofListener(_Listener): + """Special listener implementation for setting composite oneof fields.""" + + def __init__(self, parent_message, field): + """Args: + parent_message: The message whose _Modified() method we should call when + we receive Modified() messages. + field: The descriptor of the field being set in the parent message. + """ + super(_OneofListener, self).__init__(parent_message) + self._field = field + + def Modified(self): + """Also updates the state of the containing oneof in the parent message.""" + try: + self._parent_message_weakref._UpdateOneofState(self._field) + super(_OneofListener, self).Modified() + except ReferenceError: + pass diff --git a/lib/protobuf/internal/type_checkers.py b/lib/protobuf/internal/type_checkers.py new file mode 100644 index 0000000..a53e71f --- /dev/null +++ b/lib/protobuf/internal/type_checkers.py @@ -0,0 +1,435 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Provides type checking routines. + +This module defines type checking utilities in the forms of dictionaries: + +VALUE_CHECKERS: A dictionary of field types and a value validation object. +TYPE_TO_BYTE_SIZE_FN: A dictionary with field types and a size computing + function. +TYPE_TO_SERIALIZE_METHOD: A dictionary with field types and serialization + function. +FIELD_TYPE_TO_WIRE_TYPE: A dictionary with field typed and their + corresponding wire types. +TYPE_TO_DESERIALIZE_METHOD: A dictionary with field types and deserialization + function. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import ctypes +import numbers + +from google.protobuf.internal import decoder +from google.protobuf.internal import encoder +from google.protobuf.internal import wire_format +from google.protobuf import descriptor + +_FieldDescriptor = descriptor.FieldDescriptor + + +def TruncateToFourByteFloat(original): + return ctypes.c_float(original).value + + +def ToShortestFloat(original): + """Returns the shortest float that has same value in wire.""" + # All 4 byte floats have between 6 and 9 significant digits, so we + # start with 6 as the lower bound. + # It has to be iterative because use '.9g' directly can not get rid + # of the noises for most values. For example if set a float_field=0.9 + # use '.9g' will print 0.899999976. + precision = 6 + rounded = float('{0:.{1}g}'.format(original, precision)) + while TruncateToFourByteFloat(rounded) != original: + precision += 1 + rounded = float('{0:.{1}g}'.format(original, precision)) + return rounded + + +def SupportsOpenEnums(field_descriptor): + return field_descriptor.containing_type.syntax == 'proto3' + + +def GetTypeChecker(field): + """Returns a type checker for a message field of the specified types. + + Args: + field: FieldDescriptor object for this field. + + Returns: + An instance of TypeChecker which can be used to verify the types + of values assigned to a field of the specified type. + """ + if (field.cpp_type == _FieldDescriptor.CPPTYPE_STRING and + field.type == _FieldDescriptor.TYPE_STRING): + return UnicodeValueChecker() + if field.cpp_type == _FieldDescriptor.CPPTYPE_ENUM: + if SupportsOpenEnums(field): + # When open enums are supported, any int32 can be assigned. + return _VALUE_CHECKERS[_FieldDescriptor.CPPTYPE_INT32] + else: + return EnumValueChecker(field.enum_type) + return _VALUE_CHECKERS[field.cpp_type] + + +# None of the typecheckers below make any attempt to guard against people +# subclassing builtin types and doing weird things. We're not trying to +# protect against malicious clients here, just people accidentally shooting +# themselves in the foot in obvious ways. +class TypeChecker(object): + + """Type checker used to catch type errors as early as possible + when the client is setting scalar fields in protocol messages. + """ + + def __init__(self, *acceptable_types): + self._acceptable_types = acceptable_types + + def CheckValue(self, proposed_value): + """Type check the provided value and return it. + + The returned value might have been normalized to another type. + """ + if not isinstance(proposed_value, self._acceptable_types): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), self._acceptable_types)) + raise TypeError(message) + return proposed_value + + +class TypeCheckerWithDefault(TypeChecker): + + def __init__(self, default_value, *acceptable_types): + TypeChecker.__init__(self, *acceptable_types) + self._default_value = default_value + + def DefaultValue(self): + return self._default_value + + +class BoolValueChecker(object): + """Type checker used for bool fields.""" + + def CheckValue(self, proposed_value): + if not hasattr(proposed_value, '__index__') or ( + type(proposed_value).__module__ == 'numpy' and + type(proposed_value).__name__ == 'ndarray'): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), (bool, int))) + raise TypeError(message) + return bool(proposed_value) + + def DefaultValue(self): + return False + + +# IntValueChecker and its subclasses perform integer type-checks +# and bounds-checks. +class IntValueChecker(object): + + """Checker used for integer fields. Performs type-check and range check.""" + + def CheckValue(self, proposed_value): + if not hasattr(proposed_value, '__index__') or ( + type(proposed_value).__module__ == 'numpy' and + type(proposed_value).__name__ == 'ndarray'): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), (int,))) + raise TypeError(message) + + if not self._MIN <= int(proposed_value) <= self._MAX: + raise ValueError('Value out of range: %d' % proposed_value) + # We force all values to int to make alternate implementations where the + # distinction is more significant (e.g. the C++ implementation) simpler. + proposed_value = int(proposed_value) + return proposed_value + + def DefaultValue(self): + return 0 + + +class EnumValueChecker(object): + + """Checker used for enum fields. Performs type-check and range check.""" + + def __init__(self, enum_type): + self._enum_type = enum_type + + def CheckValue(self, proposed_value): + if not isinstance(proposed_value, numbers.Integral): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), (int,))) + raise TypeError(message) + if int(proposed_value) not in self._enum_type.values_by_number: + raise ValueError('Unknown enum value: %d' % proposed_value) + return proposed_value + + def DefaultValue(self): + return self._enum_type.values[0].number + + +class UnicodeValueChecker(object): + + """Checker used for string fields. + + Always returns a unicode value, even if the input is of type str. + """ + + def CheckValue(self, proposed_value): + if not isinstance(proposed_value, (bytes, str)): + message = ('%.1024r has type %s, but expected one of: %s' % + (proposed_value, type(proposed_value), (bytes, str))) + raise TypeError(message) + + # If the value is of type 'bytes' make sure that it is valid UTF-8 data. + if isinstance(proposed_value, bytes): + try: + proposed_value = proposed_value.decode('utf-8') + except UnicodeDecodeError: + raise ValueError('%.1024r has type bytes, but isn\'t valid UTF-8 ' + 'encoding. Non-UTF-8 strings must be converted to ' + 'unicode objects before being added.' % + (proposed_value)) + else: + try: + proposed_value.encode('utf8') + except UnicodeEncodeError: + raise ValueError('%.1024r isn\'t a valid unicode string and ' + 'can\'t be encoded in UTF-8.'% + (proposed_value)) + + return proposed_value + + def DefaultValue(self): + return u"" + + +class Int32ValueChecker(IntValueChecker): + # We're sure to use ints instead of longs here since comparison may be more + # efficient. + _MIN = -2147483648 + _MAX = 2147483647 + + +class Uint32ValueChecker(IntValueChecker): + _MIN = 0 + _MAX = (1 << 32) - 1 + + +class Int64ValueChecker(IntValueChecker): + _MIN = -(1 << 63) + _MAX = (1 << 63) - 1 + + +class Uint64ValueChecker(IntValueChecker): + _MIN = 0 + _MAX = (1 << 64) - 1 + + +# The max 4 bytes float is about 3.4028234663852886e+38 +_FLOAT_MAX = float.fromhex('0x1.fffffep+127') +_FLOAT_MIN = -_FLOAT_MAX +_INF = float('inf') +_NEG_INF = float('-inf') + + +class DoubleValueChecker(object): + """Checker used for double fields. + + Performs type-check and range check. + """ + + def CheckValue(self, proposed_value): + """Check and convert proposed_value to float.""" + if (not hasattr(proposed_value, '__float__') and + not hasattr(proposed_value, '__index__')) or ( + type(proposed_value).__module__ == 'numpy' and + type(proposed_value).__name__ == 'ndarray'): + message = ('%.1024r has type %s, but expected one of: int, float' % + (proposed_value, type(proposed_value))) + raise TypeError(message) + return float(proposed_value) + + def DefaultValue(self): + return 0.0 + + +class FloatValueChecker(DoubleValueChecker): + """Checker used for float fields. + + Performs type-check and range check. + + Values exceeding a 32-bit float will be converted to inf/-inf. + """ + + def CheckValue(self, proposed_value): + """Check and convert proposed_value to float.""" + converted_value = super().CheckValue(proposed_value) + # This inf rounding matches the C++ proto SafeDoubleToFloat logic. + if converted_value > _FLOAT_MAX: + return _INF + if converted_value < _FLOAT_MIN: + return _NEG_INF + + return TruncateToFourByteFloat(converted_value) + +# Type-checkers for all scalar CPPTYPEs. +_VALUE_CHECKERS = { + _FieldDescriptor.CPPTYPE_INT32: Int32ValueChecker(), + _FieldDescriptor.CPPTYPE_INT64: Int64ValueChecker(), + _FieldDescriptor.CPPTYPE_UINT32: Uint32ValueChecker(), + _FieldDescriptor.CPPTYPE_UINT64: Uint64ValueChecker(), + _FieldDescriptor.CPPTYPE_DOUBLE: DoubleValueChecker(), + _FieldDescriptor.CPPTYPE_FLOAT: FloatValueChecker(), + _FieldDescriptor.CPPTYPE_BOOL: BoolValueChecker(), + _FieldDescriptor.CPPTYPE_STRING: TypeCheckerWithDefault(b'', bytes), +} + + +# Map from field type to a function F, such that F(field_num, value) +# gives the total byte size for a value of the given type. This +# byte size includes tag information and any other additional space +# associated with serializing "value". +TYPE_TO_BYTE_SIZE_FN = { + _FieldDescriptor.TYPE_DOUBLE: wire_format.DoubleByteSize, + _FieldDescriptor.TYPE_FLOAT: wire_format.FloatByteSize, + _FieldDescriptor.TYPE_INT64: wire_format.Int64ByteSize, + _FieldDescriptor.TYPE_UINT64: wire_format.UInt64ByteSize, + _FieldDescriptor.TYPE_INT32: wire_format.Int32ByteSize, + _FieldDescriptor.TYPE_FIXED64: wire_format.Fixed64ByteSize, + _FieldDescriptor.TYPE_FIXED32: wire_format.Fixed32ByteSize, + _FieldDescriptor.TYPE_BOOL: wire_format.BoolByteSize, + _FieldDescriptor.TYPE_STRING: wire_format.StringByteSize, + _FieldDescriptor.TYPE_GROUP: wire_format.GroupByteSize, + _FieldDescriptor.TYPE_MESSAGE: wire_format.MessageByteSize, + _FieldDescriptor.TYPE_BYTES: wire_format.BytesByteSize, + _FieldDescriptor.TYPE_UINT32: wire_format.UInt32ByteSize, + _FieldDescriptor.TYPE_ENUM: wire_format.EnumByteSize, + _FieldDescriptor.TYPE_SFIXED32: wire_format.SFixed32ByteSize, + _FieldDescriptor.TYPE_SFIXED64: wire_format.SFixed64ByteSize, + _FieldDescriptor.TYPE_SINT32: wire_format.SInt32ByteSize, + _FieldDescriptor.TYPE_SINT64: wire_format.SInt64ByteSize + } + + +# Maps from field types to encoder constructors. +TYPE_TO_ENCODER = { + _FieldDescriptor.TYPE_DOUBLE: encoder.DoubleEncoder, + _FieldDescriptor.TYPE_FLOAT: encoder.FloatEncoder, + _FieldDescriptor.TYPE_INT64: encoder.Int64Encoder, + _FieldDescriptor.TYPE_UINT64: encoder.UInt64Encoder, + _FieldDescriptor.TYPE_INT32: encoder.Int32Encoder, + _FieldDescriptor.TYPE_FIXED64: encoder.Fixed64Encoder, + _FieldDescriptor.TYPE_FIXED32: encoder.Fixed32Encoder, + _FieldDescriptor.TYPE_BOOL: encoder.BoolEncoder, + _FieldDescriptor.TYPE_STRING: encoder.StringEncoder, + _FieldDescriptor.TYPE_GROUP: encoder.GroupEncoder, + _FieldDescriptor.TYPE_MESSAGE: encoder.MessageEncoder, + _FieldDescriptor.TYPE_BYTES: encoder.BytesEncoder, + _FieldDescriptor.TYPE_UINT32: encoder.UInt32Encoder, + _FieldDescriptor.TYPE_ENUM: encoder.EnumEncoder, + _FieldDescriptor.TYPE_SFIXED32: encoder.SFixed32Encoder, + _FieldDescriptor.TYPE_SFIXED64: encoder.SFixed64Encoder, + _FieldDescriptor.TYPE_SINT32: encoder.SInt32Encoder, + _FieldDescriptor.TYPE_SINT64: encoder.SInt64Encoder, + } + + +# Maps from field types to sizer constructors. +TYPE_TO_SIZER = { + _FieldDescriptor.TYPE_DOUBLE: encoder.DoubleSizer, + _FieldDescriptor.TYPE_FLOAT: encoder.FloatSizer, + _FieldDescriptor.TYPE_INT64: encoder.Int64Sizer, + _FieldDescriptor.TYPE_UINT64: encoder.UInt64Sizer, + _FieldDescriptor.TYPE_INT32: encoder.Int32Sizer, + _FieldDescriptor.TYPE_FIXED64: encoder.Fixed64Sizer, + _FieldDescriptor.TYPE_FIXED32: encoder.Fixed32Sizer, + _FieldDescriptor.TYPE_BOOL: encoder.BoolSizer, + _FieldDescriptor.TYPE_STRING: encoder.StringSizer, + _FieldDescriptor.TYPE_GROUP: encoder.GroupSizer, + _FieldDescriptor.TYPE_MESSAGE: encoder.MessageSizer, + _FieldDescriptor.TYPE_BYTES: encoder.BytesSizer, + _FieldDescriptor.TYPE_UINT32: encoder.UInt32Sizer, + _FieldDescriptor.TYPE_ENUM: encoder.EnumSizer, + _FieldDescriptor.TYPE_SFIXED32: encoder.SFixed32Sizer, + _FieldDescriptor.TYPE_SFIXED64: encoder.SFixed64Sizer, + _FieldDescriptor.TYPE_SINT32: encoder.SInt32Sizer, + _FieldDescriptor.TYPE_SINT64: encoder.SInt64Sizer, + } + + +# Maps from field type to a decoder constructor. +TYPE_TO_DECODER = { + _FieldDescriptor.TYPE_DOUBLE: decoder.DoubleDecoder, + _FieldDescriptor.TYPE_FLOAT: decoder.FloatDecoder, + _FieldDescriptor.TYPE_INT64: decoder.Int64Decoder, + _FieldDescriptor.TYPE_UINT64: decoder.UInt64Decoder, + _FieldDescriptor.TYPE_INT32: decoder.Int32Decoder, + _FieldDescriptor.TYPE_FIXED64: decoder.Fixed64Decoder, + _FieldDescriptor.TYPE_FIXED32: decoder.Fixed32Decoder, + _FieldDescriptor.TYPE_BOOL: decoder.BoolDecoder, + _FieldDescriptor.TYPE_STRING: decoder.StringDecoder, + _FieldDescriptor.TYPE_GROUP: decoder.GroupDecoder, + _FieldDescriptor.TYPE_MESSAGE: decoder.MessageDecoder, + _FieldDescriptor.TYPE_BYTES: decoder.BytesDecoder, + _FieldDescriptor.TYPE_UINT32: decoder.UInt32Decoder, + _FieldDescriptor.TYPE_ENUM: decoder.EnumDecoder, + _FieldDescriptor.TYPE_SFIXED32: decoder.SFixed32Decoder, + _FieldDescriptor.TYPE_SFIXED64: decoder.SFixed64Decoder, + _FieldDescriptor.TYPE_SINT32: decoder.SInt32Decoder, + _FieldDescriptor.TYPE_SINT64: decoder.SInt64Decoder, + } + +# Maps from field type to expected wiretype. +FIELD_TYPE_TO_WIRE_TYPE = { + _FieldDescriptor.TYPE_DOUBLE: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_FLOAT: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_INT64: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_UINT64: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_INT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_FIXED64: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_FIXED32: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_BOOL: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_STRING: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_GROUP: wire_format.WIRETYPE_START_GROUP, + _FieldDescriptor.TYPE_MESSAGE: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_BYTES: + wire_format.WIRETYPE_LENGTH_DELIMITED, + _FieldDescriptor.TYPE_UINT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_ENUM: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_SFIXED32: wire_format.WIRETYPE_FIXED32, + _FieldDescriptor.TYPE_SFIXED64: wire_format.WIRETYPE_FIXED64, + _FieldDescriptor.TYPE_SINT32: wire_format.WIRETYPE_VARINT, + _FieldDescriptor.TYPE_SINT64: wire_format.WIRETYPE_VARINT, + } diff --git a/lib/protobuf/internal/well_known_types.py b/lib/protobuf/internal/well_known_types.py new file mode 100644 index 0000000..b581ab7 --- /dev/null +++ b/lib/protobuf/internal/well_known_types.py @@ -0,0 +1,878 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Contains well known classes. + +This files defines well known classes which need extra maintenance including: + - Any + - Duration + - FieldMask + - Struct + - Timestamp +""" + +__author__ = 'jieluo@google.com (Jie Luo)' + +import calendar +import collections.abc +import datetime + +from google.protobuf.descriptor import FieldDescriptor + +_TIMESTAMPFOMAT = '%Y-%m-%dT%H:%M:%S' +_NANOS_PER_SECOND = 1000000000 +_NANOS_PER_MILLISECOND = 1000000 +_NANOS_PER_MICROSECOND = 1000 +_MILLIS_PER_SECOND = 1000 +_MICROS_PER_SECOND = 1000000 +_SECONDS_PER_DAY = 24 * 3600 +_DURATION_SECONDS_MAX = 315576000000 + + +class Any(object): + """Class for Any Message type.""" + + __slots__ = () + + def Pack(self, msg, type_url_prefix='type.googleapis.com/', + deterministic=None): + """Packs the specified message into current Any message.""" + if len(type_url_prefix) < 1 or type_url_prefix[-1] != '/': + self.type_url = '%s/%s' % (type_url_prefix, msg.DESCRIPTOR.full_name) + else: + self.type_url = '%s%s' % (type_url_prefix, msg.DESCRIPTOR.full_name) + self.value = msg.SerializeToString(deterministic=deterministic) + + def Unpack(self, msg): + """Unpacks the current Any message into specified message.""" + descriptor = msg.DESCRIPTOR + if not self.Is(descriptor): + return False + msg.ParseFromString(self.value) + return True + + def TypeName(self): + """Returns the protobuf type name of the inner message.""" + # Only last part is to be used: b/25630112 + return self.type_url.split('/')[-1] + + def Is(self, descriptor): + """Checks if this Any represents the given protobuf type.""" + return '/' in self.type_url and self.TypeName() == descriptor.full_name + + +_EPOCH_DATETIME_NAIVE = datetime.datetime.utcfromtimestamp(0) +_EPOCH_DATETIME_AWARE = datetime.datetime.fromtimestamp( + 0, tz=datetime.timezone.utc) + + +class Timestamp(object): + """Class for Timestamp message type.""" + + __slots__ = () + + def ToJsonString(self): + """Converts Timestamp to RFC 3339 date string format. + + Returns: + A string converted from timestamp. The string is always Z-normalized + and uses 3, 6 or 9 fractional digits as required to represent the + exact time. Example of the return format: '1972-01-01T10:00:20.021Z' + """ + nanos = self.nanos % _NANOS_PER_SECOND + total_sec = self.seconds + (self.nanos - nanos) // _NANOS_PER_SECOND + seconds = total_sec % _SECONDS_PER_DAY + days = (total_sec - seconds) // _SECONDS_PER_DAY + dt = datetime.datetime(1970, 1, 1) + datetime.timedelta(days, seconds) + + result = dt.isoformat() + if (nanos % 1e9) == 0: + # If there are 0 fractional digits, the fractional + # point '.' should be omitted when serializing. + return result + 'Z' + if (nanos % 1e6) == 0: + # Serialize 3 fractional digits. + return result + '.%03dZ' % (nanos / 1e6) + if (nanos % 1e3) == 0: + # Serialize 6 fractional digits. + return result + '.%06dZ' % (nanos / 1e3) + # Serialize 9 fractional digits. + return result + '.%09dZ' % nanos + + def FromJsonString(self, value): + """Parse a RFC 3339 date string format to Timestamp. + + Args: + value: A date string. Any fractional digits (or none) and any offset are + accepted as long as they fit into nano-seconds precision. + Example of accepted format: '1972-01-01T10:00:20.021-05:00' + + Raises: + ValueError: On parsing problems. + """ + if not isinstance(value, str): + raise ValueError('Timestamp JSON value not a string: {!r}'.format(value)) + timezone_offset = value.find('Z') + if timezone_offset == -1: + timezone_offset = value.find('+') + if timezone_offset == -1: + timezone_offset = value.rfind('-') + if timezone_offset == -1: + raise ValueError( + 'Failed to parse timestamp: missing valid timezone offset.') + time_value = value[0:timezone_offset] + # Parse datetime and nanos. + point_position = time_value.find('.') + if point_position == -1: + second_value = time_value + nano_value = '' + else: + second_value = time_value[:point_position] + nano_value = time_value[point_position + 1:] + if 't' in second_value: + raise ValueError( + 'time data \'{0}\' does not match format \'%Y-%m-%dT%H:%M:%S\', ' + 'lowercase \'t\' is not accepted'.format(second_value)) + date_object = datetime.datetime.strptime(second_value, _TIMESTAMPFOMAT) + td = date_object - datetime.datetime(1970, 1, 1) + seconds = td.seconds + td.days * _SECONDS_PER_DAY + if len(nano_value) > 9: + raise ValueError( + 'Failed to parse Timestamp: nanos {0} more than ' + '9 fractional digits.'.format(nano_value)) + if nano_value: + nanos = round(float('0.' + nano_value) * 1e9) + else: + nanos = 0 + # Parse timezone offsets. + if value[timezone_offset] == 'Z': + if len(value) != timezone_offset + 1: + raise ValueError('Failed to parse timestamp: invalid trailing' + ' data {0}.'.format(value)) + else: + timezone = value[timezone_offset:] + pos = timezone.find(':') + if pos == -1: + raise ValueError( + 'Invalid timezone offset value: {0}.'.format(timezone)) + if timezone[0] == '+': + seconds -= (int(timezone[1:pos])*60+int(timezone[pos+1:]))*60 + else: + seconds += (int(timezone[1:pos])*60+int(timezone[pos+1:]))*60 + # Set seconds and nanos + self.seconds = int(seconds) + self.nanos = int(nanos) + + def GetCurrentTime(self): + """Get the current UTC into Timestamp.""" + self.FromDatetime(datetime.datetime.utcnow()) + + def ToNanoseconds(self): + """Converts Timestamp to nanoseconds since epoch.""" + return self.seconds * _NANOS_PER_SECOND + self.nanos + + def ToMicroseconds(self): + """Converts Timestamp to microseconds since epoch.""" + return (self.seconds * _MICROS_PER_SECOND + + self.nanos // _NANOS_PER_MICROSECOND) + + def ToMilliseconds(self): + """Converts Timestamp to milliseconds since epoch.""" + return (self.seconds * _MILLIS_PER_SECOND + + self.nanos // _NANOS_PER_MILLISECOND) + + def ToSeconds(self): + """Converts Timestamp to seconds since epoch.""" + return self.seconds + + def FromNanoseconds(self, nanos): + """Converts nanoseconds since epoch to Timestamp.""" + self.seconds = nanos // _NANOS_PER_SECOND + self.nanos = nanos % _NANOS_PER_SECOND + + def FromMicroseconds(self, micros): + """Converts microseconds since epoch to Timestamp.""" + self.seconds = micros // _MICROS_PER_SECOND + self.nanos = (micros % _MICROS_PER_SECOND) * _NANOS_PER_MICROSECOND + + def FromMilliseconds(self, millis): + """Converts milliseconds since epoch to Timestamp.""" + self.seconds = millis // _MILLIS_PER_SECOND + self.nanos = (millis % _MILLIS_PER_SECOND) * _NANOS_PER_MILLISECOND + + def FromSeconds(self, seconds): + """Converts seconds since epoch to Timestamp.""" + self.seconds = seconds + self.nanos = 0 + + def ToDatetime(self, tzinfo=None): + """Converts Timestamp to a datetime. + + Args: + tzinfo: A datetime.tzinfo subclass; defaults to None. + + Returns: + If tzinfo is None, returns a timezone-naive UTC datetime (with no timezone + information, i.e. not aware that it's UTC). + + Otherwise, returns a timezone-aware datetime in the input timezone. + """ + delta = datetime.timedelta( + seconds=self.seconds, + microseconds=_RoundTowardZero(self.nanos, _NANOS_PER_MICROSECOND)) + if tzinfo is None: + return _EPOCH_DATETIME_NAIVE + delta + else: + return _EPOCH_DATETIME_AWARE.astimezone(tzinfo) + delta + + def FromDatetime(self, dt): + """Converts datetime to Timestamp. + + Args: + dt: A datetime. If it's timezone-naive, it's assumed to be in UTC. + """ + # Using this guide: http://wiki.python.org/moin/WorkingWithTime + # And this conversion guide: http://docs.python.org/library/time.html + + # Turn the date parameter into a tuple (struct_time) that can then be + # manipulated into a long value of seconds. During the conversion from + # struct_time to long, the source date in UTC, and so it follows that the + # correct transformation is calendar.timegm() + self.seconds = calendar.timegm(dt.utctimetuple()) + self.nanos = dt.microsecond * _NANOS_PER_MICROSECOND + + +class Duration(object): + """Class for Duration message type.""" + + __slots__ = () + + def ToJsonString(self): + """Converts Duration to string format. + + Returns: + A string converted from self. The string format will contains + 3, 6, or 9 fractional digits depending on the precision required to + represent the exact Duration value. For example: "1s", "1.010s", + "1.000000100s", "-3.100s" + """ + _CheckDurationValid(self.seconds, self.nanos) + if self.seconds < 0 or self.nanos < 0: + result = '-' + seconds = - self.seconds + int((0 - self.nanos) // 1e9) + nanos = (0 - self.nanos) % 1e9 + else: + result = '' + seconds = self.seconds + int(self.nanos // 1e9) + nanos = self.nanos % 1e9 + result += '%d' % seconds + if (nanos % 1e9) == 0: + # If there are 0 fractional digits, the fractional + # point '.' should be omitted when serializing. + return result + 's' + if (nanos % 1e6) == 0: + # Serialize 3 fractional digits. + return result + '.%03ds' % (nanos / 1e6) + if (nanos % 1e3) == 0: + # Serialize 6 fractional digits. + return result + '.%06ds' % (nanos / 1e3) + # Serialize 9 fractional digits. + return result + '.%09ds' % nanos + + def FromJsonString(self, value): + """Converts a string to Duration. + + Args: + value: A string to be converted. The string must end with 's'. Any + fractional digits (or none) are accepted as long as they fit into + precision. For example: "1s", "1.01s", "1.0000001s", "-3.100s + + Raises: + ValueError: On parsing problems. + """ + if not isinstance(value, str): + raise ValueError('Duration JSON value not a string: {!r}'.format(value)) + if len(value) < 1 or value[-1] != 's': + raise ValueError( + 'Duration must end with letter "s": {0}.'.format(value)) + try: + pos = value.find('.') + if pos == -1: + seconds = int(value[:-1]) + nanos = 0 + else: + seconds = int(value[:pos]) + if value[0] == '-': + nanos = int(round(float('-0{0}'.format(value[pos: -1])) *1e9)) + else: + nanos = int(round(float('0{0}'.format(value[pos: -1])) *1e9)) + _CheckDurationValid(seconds, nanos) + self.seconds = seconds + self.nanos = nanos + except ValueError as e: + raise ValueError( + 'Couldn\'t parse duration: {0} : {1}.'.format(value, e)) + + def ToNanoseconds(self): + """Converts a Duration to nanoseconds.""" + return self.seconds * _NANOS_PER_SECOND + self.nanos + + def ToMicroseconds(self): + """Converts a Duration to microseconds.""" + micros = _RoundTowardZero(self.nanos, _NANOS_PER_MICROSECOND) + return self.seconds * _MICROS_PER_SECOND + micros + + def ToMilliseconds(self): + """Converts a Duration to milliseconds.""" + millis = _RoundTowardZero(self.nanos, _NANOS_PER_MILLISECOND) + return self.seconds * _MILLIS_PER_SECOND + millis + + def ToSeconds(self): + """Converts a Duration to seconds.""" + return self.seconds + + def FromNanoseconds(self, nanos): + """Converts nanoseconds to Duration.""" + self._NormalizeDuration(nanos // _NANOS_PER_SECOND, + nanos % _NANOS_PER_SECOND) + + def FromMicroseconds(self, micros): + """Converts microseconds to Duration.""" + self._NormalizeDuration( + micros // _MICROS_PER_SECOND, + (micros % _MICROS_PER_SECOND) * _NANOS_PER_MICROSECOND) + + def FromMilliseconds(self, millis): + """Converts milliseconds to Duration.""" + self._NormalizeDuration( + millis // _MILLIS_PER_SECOND, + (millis % _MILLIS_PER_SECOND) * _NANOS_PER_MILLISECOND) + + def FromSeconds(self, seconds): + """Converts seconds to Duration.""" + self.seconds = seconds + self.nanos = 0 + + def ToTimedelta(self): + """Converts Duration to timedelta.""" + return datetime.timedelta( + seconds=self.seconds, microseconds=_RoundTowardZero( + self.nanos, _NANOS_PER_MICROSECOND)) + + def FromTimedelta(self, td): + """Converts timedelta to Duration.""" + self._NormalizeDuration(td.seconds + td.days * _SECONDS_PER_DAY, + td.microseconds * _NANOS_PER_MICROSECOND) + + def _NormalizeDuration(self, seconds, nanos): + """Set Duration by seconds and nanos.""" + # Force nanos to be negative if the duration is negative. + if seconds < 0 and nanos > 0: + seconds += 1 + nanos -= _NANOS_PER_SECOND + self.seconds = seconds + self.nanos = nanos + + +def _CheckDurationValid(seconds, nanos): + if seconds < -_DURATION_SECONDS_MAX or seconds > _DURATION_SECONDS_MAX: + raise ValueError( + 'Duration is not valid: Seconds {0} must be in range ' + '[-315576000000, 315576000000].'.format(seconds)) + if nanos <= -_NANOS_PER_SECOND or nanos >= _NANOS_PER_SECOND: + raise ValueError( + 'Duration is not valid: Nanos {0} must be in range ' + '[-999999999, 999999999].'.format(nanos)) + if (nanos < 0 and seconds > 0) or (nanos > 0 and seconds < 0): + raise ValueError( + 'Duration is not valid: Sign mismatch.') + + +def _RoundTowardZero(value, divider): + """Truncates the remainder part after division.""" + # For some languages, the sign of the remainder is implementation + # dependent if any of the operands is negative. Here we enforce + # "rounded toward zero" semantics. For example, for (-5) / 2 an + # implementation may give -3 as the result with the remainder being + # 1. This function ensures we always return -2 (closer to zero). + result = value // divider + remainder = value % divider + if result < 0 and remainder > 0: + return result + 1 + else: + return result + + +class FieldMask(object): + """Class for FieldMask message type.""" + + __slots__ = () + + def ToJsonString(self): + """Converts FieldMask to string according to proto3 JSON spec.""" + camelcase_paths = [] + for path in self.paths: + camelcase_paths.append(_SnakeCaseToCamelCase(path)) + return ','.join(camelcase_paths) + + def FromJsonString(self, value): + """Converts string to FieldMask according to proto3 JSON spec.""" + if not isinstance(value, str): + raise ValueError('FieldMask JSON value not a string: {!r}'.format(value)) + self.Clear() + if value: + for path in value.split(','): + self.paths.append(_CamelCaseToSnakeCase(path)) + + def IsValidForDescriptor(self, message_descriptor): + """Checks whether the FieldMask is valid for Message Descriptor.""" + for path in self.paths: + if not _IsValidPath(message_descriptor, path): + return False + return True + + def AllFieldsFromDescriptor(self, message_descriptor): + """Gets all direct fields of Message Descriptor to FieldMask.""" + self.Clear() + for field in message_descriptor.fields: + self.paths.append(field.name) + + def CanonicalFormFromMask(self, mask): + """Converts a FieldMask to the canonical form. + + Removes paths that are covered by another path. For example, + "foo.bar" is covered by "foo" and will be removed if "foo" + is also in the FieldMask. Then sorts all paths in alphabetical order. + + Args: + mask: The original FieldMask to be converted. + """ + tree = _FieldMaskTree(mask) + tree.ToFieldMask(self) + + def Union(self, mask1, mask2): + """Merges mask1 and mask2 into this FieldMask.""" + _CheckFieldMaskMessage(mask1) + _CheckFieldMaskMessage(mask2) + tree = _FieldMaskTree(mask1) + tree.MergeFromFieldMask(mask2) + tree.ToFieldMask(self) + + def Intersect(self, mask1, mask2): + """Intersects mask1 and mask2 into this FieldMask.""" + _CheckFieldMaskMessage(mask1) + _CheckFieldMaskMessage(mask2) + tree = _FieldMaskTree(mask1) + intersection = _FieldMaskTree() + for path in mask2.paths: + tree.IntersectPath(path, intersection) + intersection.ToFieldMask(self) + + def MergeMessage( + self, source, destination, + replace_message_field=False, replace_repeated_field=False): + """Merges fields specified in FieldMask from source to destination. + + Args: + source: Source message. + destination: The destination message to be merged into. + replace_message_field: Replace message field if True. Merge message + field if False. + replace_repeated_field: Replace repeated field if True. Append + elements of repeated field if False. + """ + tree = _FieldMaskTree(self) + tree.MergeMessage( + source, destination, replace_message_field, replace_repeated_field) + + +def _IsValidPath(message_descriptor, path): + """Checks whether the path is valid for Message Descriptor.""" + parts = path.split('.') + last = parts.pop() + for name in parts: + field = message_descriptor.fields_by_name.get(name) + if (field is None or + field.label == FieldDescriptor.LABEL_REPEATED or + field.type != FieldDescriptor.TYPE_MESSAGE): + return False + message_descriptor = field.message_type + return last in message_descriptor.fields_by_name + + +def _CheckFieldMaskMessage(message): + """Raises ValueError if message is not a FieldMask.""" + message_descriptor = message.DESCRIPTOR + if (message_descriptor.name != 'FieldMask' or + message_descriptor.file.name != 'google/protobuf/field_mask.proto'): + raise ValueError('Message {0} is not a FieldMask.'.format( + message_descriptor.full_name)) + + +def _SnakeCaseToCamelCase(path_name): + """Converts a path name from snake_case to camelCase.""" + result = [] + after_underscore = False + for c in path_name: + if c.isupper(): + raise ValueError( + 'Fail to print FieldMask to Json string: Path name ' + '{0} must not contain uppercase letters.'.format(path_name)) + if after_underscore: + if c.islower(): + result.append(c.upper()) + after_underscore = False + else: + raise ValueError( + 'Fail to print FieldMask to Json string: The ' + 'character after a "_" must be a lowercase letter ' + 'in path name {0}.'.format(path_name)) + elif c == '_': + after_underscore = True + else: + result += c + + if after_underscore: + raise ValueError('Fail to print FieldMask to Json string: Trailing "_" ' + 'in path name {0}.'.format(path_name)) + return ''.join(result) + + +def _CamelCaseToSnakeCase(path_name): + """Converts a field name from camelCase to snake_case.""" + result = [] + for c in path_name: + if c == '_': + raise ValueError('Fail to parse FieldMask: Path name ' + '{0} must not contain "_"s.'.format(path_name)) + if c.isupper(): + result += '_' + result += c.lower() + else: + result += c + return ''.join(result) + + +class _FieldMaskTree(object): + """Represents a FieldMask in a tree structure. + + For example, given a FieldMask "foo.bar,foo.baz,bar.baz", + the FieldMaskTree will be: + [_root] -+- foo -+- bar + | | + | +- baz + | + +- bar --- baz + In the tree, each leaf node represents a field path. + """ + + __slots__ = ('_root',) + + def __init__(self, field_mask=None): + """Initializes the tree by FieldMask.""" + self._root = {} + if field_mask: + self.MergeFromFieldMask(field_mask) + + def MergeFromFieldMask(self, field_mask): + """Merges a FieldMask to the tree.""" + for path in field_mask.paths: + self.AddPath(path) + + def AddPath(self, path): + """Adds a field path into the tree. + + If the field path to add is a sub-path of an existing field path + in the tree (i.e., a leaf node), it means the tree already matches + the given path so nothing will be added to the tree. If the path + matches an existing non-leaf node in the tree, that non-leaf node + will be turned into a leaf node with all its children removed because + the path matches all the node's children. Otherwise, a new path will + be added. + + Args: + path: The field path to add. + """ + node = self._root + for name in path.split('.'): + if name not in node: + node[name] = {} + elif not node[name]: + # Pre-existing empty node implies we already have this entire tree. + return + node = node[name] + # Remove any sub-trees we might have had. + node.clear() + + def ToFieldMask(self, field_mask): + """Converts the tree to a FieldMask.""" + field_mask.Clear() + _AddFieldPaths(self._root, '', field_mask) + + def IntersectPath(self, path, intersection): + """Calculates the intersection part of a field path with this tree. + + Args: + path: The field path to calculates. + intersection: The out tree to record the intersection part. + """ + node = self._root + for name in path.split('.'): + if name not in node: + return + elif not node[name]: + intersection.AddPath(path) + return + node = node[name] + intersection.AddLeafNodes(path, node) + + def AddLeafNodes(self, prefix, node): + """Adds leaf nodes begin with prefix to this tree.""" + if not node: + self.AddPath(prefix) + for name in node: + child_path = prefix + '.' + name + self.AddLeafNodes(child_path, node[name]) + + def MergeMessage( + self, source, destination, + replace_message, replace_repeated): + """Merge all fields specified by this tree from source to destination.""" + _MergeMessage( + self._root, source, destination, replace_message, replace_repeated) + + +def _StrConvert(value): + """Converts value to str if it is not.""" + # This file is imported by c extension and some methods like ClearField + # requires string for the field name. py2/py3 has different text + # type and may use unicode. + if not isinstance(value, str): + return value.encode('utf-8') + return value + + +def _MergeMessage( + node, source, destination, replace_message, replace_repeated): + """Merge all fields specified by a sub-tree from source to destination.""" + source_descriptor = source.DESCRIPTOR + for name in node: + child = node[name] + field = source_descriptor.fields_by_name[name] + if field is None: + raise ValueError('Error: Can\'t find field {0} in message {1}.'.format( + name, source_descriptor.full_name)) + if child: + # Sub-paths are only allowed for singular message fields. + if (field.label == FieldDescriptor.LABEL_REPEATED or + field.cpp_type != FieldDescriptor.CPPTYPE_MESSAGE): + raise ValueError('Error: Field {0} in message {1} is not a singular ' + 'message field and cannot have sub-fields.'.format( + name, source_descriptor.full_name)) + if source.HasField(name): + _MergeMessage( + child, getattr(source, name), getattr(destination, name), + replace_message, replace_repeated) + continue + if field.label == FieldDescriptor.LABEL_REPEATED: + if replace_repeated: + destination.ClearField(_StrConvert(name)) + repeated_source = getattr(source, name) + repeated_destination = getattr(destination, name) + repeated_destination.MergeFrom(repeated_source) + else: + if field.cpp_type == FieldDescriptor.CPPTYPE_MESSAGE: + if replace_message: + destination.ClearField(_StrConvert(name)) + if source.HasField(name): + getattr(destination, name).MergeFrom(getattr(source, name)) + else: + setattr(destination, name, getattr(source, name)) + + +def _AddFieldPaths(node, prefix, field_mask): + """Adds the field paths descended from node to field_mask.""" + if not node and prefix: + field_mask.paths.append(prefix) + return + for name in sorted(node): + if prefix: + child_path = prefix + '.' + name + else: + child_path = name + _AddFieldPaths(node[name], child_path, field_mask) + + +def _SetStructValue(struct_value, value): + if value is None: + struct_value.null_value = 0 + elif isinstance(value, bool): + # Note: this check must come before the number check because in Python + # True and False are also considered numbers. + struct_value.bool_value = value + elif isinstance(value, str): + struct_value.string_value = value + elif isinstance(value, (int, float)): + struct_value.number_value = value + elif isinstance(value, (dict, Struct)): + struct_value.struct_value.Clear() + struct_value.struct_value.update(value) + elif isinstance(value, (list, ListValue)): + struct_value.list_value.Clear() + struct_value.list_value.extend(value) + else: + raise ValueError('Unexpected type') + + +def _GetStructValue(struct_value): + which = struct_value.WhichOneof('kind') + if which == 'struct_value': + return struct_value.struct_value + elif which == 'null_value': + return None + elif which == 'number_value': + return struct_value.number_value + elif which == 'string_value': + return struct_value.string_value + elif which == 'bool_value': + return struct_value.bool_value + elif which == 'list_value': + return struct_value.list_value + elif which is None: + raise ValueError('Value not set') + + +class Struct(object): + """Class for Struct message type.""" + + __slots__ = () + + def __getitem__(self, key): + return _GetStructValue(self.fields[key]) + + def __contains__(self, item): + return item in self.fields + + def __setitem__(self, key, value): + _SetStructValue(self.fields[key], value) + + def __delitem__(self, key): + del self.fields[key] + + def __len__(self): + return len(self.fields) + + def __iter__(self): + return iter(self.fields) + + def keys(self): # pylint: disable=invalid-name + return self.fields.keys() + + def values(self): # pylint: disable=invalid-name + return [self[key] for key in self] + + def items(self): # pylint: disable=invalid-name + return [(key, self[key]) for key in self] + + def get_or_create_list(self, key): + """Returns a list for this key, creating if it didn't exist already.""" + if not self.fields[key].HasField('list_value'): + # Clear will mark list_value modified which will indeed create a list. + self.fields[key].list_value.Clear() + return self.fields[key].list_value + + def get_or_create_struct(self, key): + """Returns a struct for this key, creating if it didn't exist already.""" + if not self.fields[key].HasField('struct_value'): + # Clear will mark struct_value modified which will indeed create a struct. + self.fields[key].struct_value.Clear() + return self.fields[key].struct_value + + def update(self, dictionary): # pylint: disable=invalid-name + for key, value in dictionary.items(): + _SetStructValue(self.fields[key], value) + +collections.abc.MutableMapping.register(Struct) + + +class ListValue(object): + """Class for ListValue message type.""" + + __slots__ = () + + def __len__(self): + return len(self.values) + + def append(self, value): + _SetStructValue(self.values.add(), value) + + def extend(self, elem_seq): + for value in elem_seq: + self.append(value) + + def __getitem__(self, index): + """Retrieves item by the specified index.""" + return _GetStructValue(self.values.__getitem__(index)) + + def __setitem__(self, index, value): + _SetStructValue(self.values.__getitem__(index), value) + + def __delitem__(self, key): + del self.values[key] + + def items(self): + for i in range(len(self)): + yield self[i] + + def add_struct(self): + """Appends and returns a struct value as the next value in the list.""" + struct_value = self.values.add().struct_value + # Clear will mark struct_value modified which will indeed create a struct. + struct_value.Clear() + return struct_value + + def add_list(self): + """Appends and returns a list value as the next value in the list.""" + list_value = self.values.add().list_value + # Clear will mark list_value modified which will indeed create a list. + list_value.Clear() + return list_value + +collections.abc.MutableSequence.register(ListValue) + + +WKTBASES = { + 'google.protobuf.Any': Any, + 'google.protobuf.Duration': Duration, + 'google.protobuf.FieldMask': FieldMask, + 'google.protobuf.ListValue': ListValue, + 'google.protobuf.Struct': Struct, + 'google.protobuf.Timestamp': Timestamp, +} diff --git a/lib/protobuf/internal/wire_format.py b/lib/protobuf/internal/wire_format.py new file mode 100644 index 0000000..883f525 --- /dev/null +++ b/lib/protobuf/internal/wire_format.py @@ -0,0 +1,268 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Constants and static functions to support protocol buffer wire format.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +import struct +from google.protobuf import descriptor +from google.protobuf import message + + +TAG_TYPE_BITS = 3 # Number of bits used to hold type info in a proto tag. +TAG_TYPE_MASK = (1 << TAG_TYPE_BITS) - 1 # 0x7 + +# These numbers identify the wire type of a protocol buffer value. +# We use the least-significant TAG_TYPE_BITS bits of the varint-encoded +# tag-and-type to store one of these WIRETYPE_* constants. +# These values must match WireType enum in google/protobuf/wire_format.h. +WIRETYPE_VARINT = 0 +WIRETYPE_FIXED64 = 1 +WIRETYPE_LENGTH_DELIMITED = 2 +WIRETYPE_START_GROUP = 3 +WIRETYPE_END_GROUP = 4 +WIRETYPE_FIXED32 = 5 +_WIRETYPE_MAX = 5 + + +# Bounds for various integer types. +INT32_MAX = int((1 << 31) - 1) +INT32_MIN = int(-(1 << 31)) +UINT32_MAX = (1 << 32) - 1 + +INT64_MAX = (1 << 63) - 1 +INT64_MIN = -(1 << 63) +UINT64_MAX = (1 << 64) - 1 + +# "struct" format strings that will encode/decode the specified formats. +FORMAT_UINT32_LITTLE_ENDIAN = '> TAG_TYPE_BITS), (tag & TAG_TYPE_MASK) + + +def ZigZagEncode(value): + """ZigZag Transform: Encodes signed integers so that they can be + effectively used with varint encoding. See wire_format.h for + more details. + """ + if value >= 0: + return value << 1 + return (value << 1) ^ (~0) + + +def ZigZagDecode(value): + """Inverse of ZigZagEncode().""" + if not value & 0x1: + return value >> 1 + return (value >> 1) ^ (~0) + + + +# The *ByteSize() functions below return the number of bytes required to +# serialize "field number + type" information and then serialize the value. + + +def Int32ByteSize(field_number, int32): + return Int64ByteSize(field_number, int32) + + +def Int32ByteSizeNoTag(int32): + return _VarUInt64ByteSizeNoTag(0xffffffffffffffff & int32) + + +def Int64ByteSize(field_number, int64): + # Have to convert to uint before calling UInt64ByteSize(). + return UInt64ByteSize(field_number, 0xffffffffffffffff & int64) + + +def UInt32ByteSize(field_number, uint32): + return UInt64ByteSize(field_number, uint32) + + +def UInt64ByteSize(field_number, uint64): + return TagByteSize(field_number) + _VarUInt64ByteSizeNoTag(uint64) + + +def SInt32ByteSize(field_number, int32): + return UInt32ByteSize(field_number, ZigZagEncode(int32)) + + +def SInt64ByteSize(field_number, int64): + return UInt64ByteSize(field_number, ZigZagEncode(int64)) + + +def Fixed32ByteSize(field_number, fixed32): + return TagByteSize(field_number) + 4 + + +def Fixed64ByteSize(field_number, fixed64): + return TagByteSize(field_number) + 8 + + +def SFixed32ByteSize(field_number, sfixed32): + return TagByteSize(field_number) + 4 + + +def SFixed64ByteSize(field_number, sfixed64): + return TagByteSize(field_number) + 8 + + +def FloatByteSize(field_number, flt): + return TagByteSize(field_number) + 4 + + +def DoubleByteSize(field_number, double): + return TagByteSize(field_number) + 8 + + +def BoolByteSize(field_number, b): + return TagByteSize(field_number) + 1 + + +def EnumByteSize(field_number, enum): + return UInt32ByteSize(field_number, enum) + + +def StringByteSize(field_number, string): + return BytesByteSize(field_number, string.encode('utf-8')) + + +def BytesByteSize(field_number, b): + return (TagByteSize(field_number) + + _VarUInt64ByteSizeNoTag(len(b)) + + len(b)) + + +def GroupByteSize(field_number, message): + return (2 * TagByteSize(field_number) # START and END group. + + message.ByteSize()) + + +def MessageByteSize(field_number, message): + return (TagByteSize(field_number) + + _VarUInt64ByteSizeNoTag(message.ByteSize()) + + message.ByteSize()) + + +def MessageSetItemByteSize(field_number, msg): + # First compute the sizes of the tags. + # There are 2 tags for the beginning and ending of the repeated group, that + # is field number 1, one with field number 2 (type_id) and one with field + # number 3 (message). + total_size = (2 * TagByteSize(1) + TagByteSize(2) + TagByteSize(3)) + + # Add the number of bytes for type_id. + total_size += _VarUInt64ByteSizeNoTag(field_number) + + message_size = msg.ByteSize() + + # The number of bytes for encoding the length of the message. + total_size += _VarUInt64ByteSizeNoTag(message_size) + + # The size of the message. + total_size += message_size + return total_size + + +def TagByteSize(field_number): + """Returns the bytes required to serialize a tag with this field number.""" + # Just pass in type 0, since the type won't affect the tag+type size. + return _VarUInt64ByteSizeNoTag(PackTag(field_number, 0)) + + +# Private helper function for the *ByteSize() functions above. + +def _VarUInt64ByteSizeNoTag(uint64): + """Returns the number of bytes required to serialize a single varint + using boundary value comparisons. (unrolled loop optimization -WPierce) + uint64 must be unsigned. + """ + if uint64 <= 0x7f: return 1 + if uint64 <= 0x3fff: return 2 + if uint64 <= 0x1fffff: return 3 + if uint64 <= 0xfffffff: return 4 + if uint64 <= 0x7ffffffff: return 5 + if uint64 <= 0x3ffffffffff: return 6 + if uint64 <= 0x1ffffffffffff: return 7 + if uint64 <= 0xffffffffffffff: return 8 + if uint64 <= 0x7fffffffffffffff: return 9 + if uint64 > UINT64_MAX: + raise message.EncodeError('Value out of range: %d' % uint64) + return 10 + + +NON_PACKABLE_TYPES = ( + descriptor.FieldDescriptor.TYPE_STRING, + descriptor.FieldDescriptor.TYPE_GROUP, + descriptor.FieldDescriptor.TYPE_MESSAGE, + descriptor.FieldDescriptor.TYPE_BYTES +) + + +def IsTypePackable(field_type): + """Return true iff packable = true is valid for fields of this type. + + Args: + field_type: a FieldDescriptor::Type value. + + Returns: + True iff fields of this type are packable. + """ + return field_type not in NON_PACKABLE_TYPES diff --git a/lib/protobuf/json_format.py b/lib/protobuf/json_format.py new file mode 100644 index 0000000..5024ed8 --- /dev/null +++ b/lib/protobuf/json_format.py @@ -0,0 +1,912 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Contains routines for printing protocol messages in JSON format. + +Simple usage example: + + # Create a proto object and serialize it to a json format string. + message = my_proto_pb2.MyMessage(foo='bar') + json_string = json_format.MessageToJson(message) + + # Parse a json format string to proto object. + message = json_format.Parse(json_string, my_proto_pb2.MyMessage()) +""" + +__author__ = 'jieluo@google.com (Jie Luo)' + + +import base64 +from collections import OrderedDict +import json +import math +from operator import methodcaller +import re +import sys + +from google.protobuf.internal import type_checkers +from google.protobuf import descriptor +from google.protobuf import symbol_database + + +_TIMESTAMPFOMAT = '%Y-%m-%dT%H:%M:%S' +_INT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT32, + descriptor.FieldDescriptor.CPPTYPE_UINT32, + descriptor.FieldDescriptor.CPPTYPE_INT64, + descriptor.FieldDescriptor.CPPTYPE_UINT64]) +_INT64_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_INT64, + descriptor.FieldDescriptor.CPPTYPE_UINT64]) +_FLOAT_TYPES = frozenset([descriptor.FieldDescriptor.CPPTYPE_FLOAT, + descriptor.FieldDescriptor.CPPTYPE_DOUBLE]) +_INFINITY = 'Infinity' +_NEG_INFINITY = '-Infinity' +_NAN = 'NaN' + +_UNPAIRED_SURROGATE_PATTERN = re.compile( + u'[\ud800-\udbff](?![\udc00-\udfff])|(? self.max_recursion_depth: + raise ParseError('Message too deep. Max recursion depth is {0}'.format( + self.max_recursion_depth)) + message_descriptor = message.DESCRIPTOR + full_name = message_descriptor.full_name + if not path: + path = message_descriptor.name + if _IsWrapperMessage(message_descriptor): + self._ConvertWrapperMessage(value, message, path) + elif full_name in _WKTJSONMETHODS: + methodcaller(_WKTJSONMETHODS[full_name][1], value, message, path)(self) + else: + self._ConvertFieldValuePair(value, message, path) + self.recursion_depth -= 1 + + def _ConvertFieldValuePair(self, js, message, path): + """Convert field value pairs into regular message. + + Args: + js: A JSON object to convert the field value pairs. + message: A regular protocol message to record the data. + path: parent path to log parse error info. + + Raises: + ParseError: In case of problems converting. + """ + names = [] + message_descriptor = message.DESCRIPTOR + fields_by_json_name = dict((f.json_name, f) + for f in message_descriptor.fields) + for name in js: + try: + field = fields_by_json_name.get(name, None) + if not field: + field = message_descriptor.fields_by_name.get(name, None) + if not field and _VALID_EXTENSION_NAME.match(name): + if not message_descriptor.is_extendable: + raise ParseError( + 'Message type {0} does not have extensions at {1}'.format( + message_descriptor.full_name, path)) + identifier = name[1:-1] # strip [] brackets + # pylint: disable=protected-access + field = message.Extensions._FindExtensionByName(identifier) + # pylint: enable=protected-access + if not field: + # Try looking for extension by the message type name, dropping the + # field name following the final . separator in full_name. + identifier = '.'.join(identifier.split('.')[:-1]) + # pylint: disable=protected-access + field = message.Extensions._FindExtensionByName(identifier) + # pylint: enable=protected-access + if not field: + if self.ignore_unknown_fields: + continue + raise ParseError( + ('Message type "{0}" has no field named "{1}" at "{2}".\n' + ' Available Fields(except extensions): "{3}"').format( + message_descriptor.full_name, name, path, + [f.json_name for f in message_descriptor.fields])) + if name in names: + raise ParseError('Message type "{0}" should not have multiple ' + '"{1}" fields at "{2}".'.format( + message.DESCRIPTOR.full_name, name, path)) + names.append(name) + value = js[name] + # Check no other oneof field is parsed. + if field.containing_oneof is not None and value is not None: + oneof_name = field.containing_oneof.name + if oneof_name in names: + raise ParseError('Message type "{0}" should not have multiple ' + '"{1}" oneof fields at "{2}".'.format( + message.DESCRIPTOR.full_name, oneof_name, + path)) + names.append(oneof_name) + + if value is None: + if (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE + and field.message_type.full_name == 'google.protobuf.Value'): + sub_message = getattr(message, field.name) + sub_message.null_value = 0 + elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM + and field.enum_type.full_name == 'google.protobuf.NullValue'): + setattr(message, field.name, 0) + else: + message.ClearField(field.name) + continue + + # Parse field value. + if _IsMapEntry(field): + message.ClearField(field.name) + self._ConvertMapFieldValue(value, message, field, + '{0}.{1}'.format(path, name)) + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + message.ClearField(field.name) + if not isinstance(value, list): + raise ParseError('repeated field {0} must be in [] which is ' + '{1} at {2}'.format(name, value, path)) + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + # Repeated message field. + for index, item in enumerate(value): + sub_message = getattr(message, field.name).add() + # None is a null_value in Value. + if (item is None and + sub_message.DESCRIPTOR.full_name != 'google.protobuf.Value'): + raise ParseError('null is not allowed to be used as an element' + ' in a repeated field at {0}.{1}[{2}]'.format( + path, name, index)) + self.ConvertMessage(item, sub_message, + '{0}.{1}[{2}]'.format(path, name, index)) + else: + # Repeated scalar field. + for index, item in enumerate(value): + if item is None: + raise ParseError('null is not allowed to be used as an element' + ' in a repeated field at {0}.{1}[{2}]'.format( + path, name, index)) + getattr(message, field.name).append( + _ConvertScalarFieldValue( + item, field, '{0}.{1}[{2}]'.format(path, name, index))) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + if field.is_extension: + sub_message = message.Extensions[field] + else: + sub_message = getattr(message, field.name) + sub_message.SetInParent() + self.ConvertMessage(value, sub_message, '{0}.{1}'.format(path, name)) + else: + if field.is_extension: + message.Extensions[field] = _ConvertScalarFieldValue( + value, field, '{0}.{1}'.format(path, name)) + else: + setattr( + message, field.name, + _ConvertScalarFieldValue(value, field, + '{0}.{1}'.format(path, name))) + except ParseError as e: + if field and field.containing_oneof is None: + raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) + else: + raise ParseError(str(e)) + except ValueError as e: + raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) + except TypeError as e: + raise ParseError('Failed to parse {0} field: {1}.'.format(name, e)) + + def _ConvertAnyMessage(self, value, message, path): + """Convert a JSON representation into Any message.""" + if isinstance(value, dict) and not value: + return + try: + type_url = value['@type'] + except KeyError: + raise ParseError( + '@type is missing when parsing any message at {0}'.format(path)) + + try: + sub_message = _CreateMessageFromTypeUrl(type_url, self.descriptor_pool) + except TypeError as e: + raise ParseError('{0} at {1}'.format(e, path)) + message_descriptor = sub_message.DESCRIPTOR + full_name = message_descriptor.full_name + if _IsWrapperMessage(message_descriptor): + self._ConvertWrapperMessage(value['value'], sub_message, + '{0}.value'.format(path)) + elif full_name in _WKTJSONMETHODS: + methodcaller(_WKTJSONMETHODS[full_name][1], value['value'], sub_message, + '{0}.value'.format(path))( + self) + else: + del value['@type'] + self._ConvertFieldValuePair(value, sub_message, path) + value['@type'] = type_url + # Sets Any message + message.value = sub_message.SerializeToString() + message.type_url = type_url + + def _ConvertGenericMessage(self, value, message, path): + """Convert a JSON representation into message with FromJsonString.""" + # Duration, Timestamp, FieldMask have a FromJsonString method to do the + # conversion. Users can also call the method directly. + try: + message.FromJsonString(value) + except ValueError as e: + raise ParseError('{0} at {1}'.format(e, path)) + + def _ConvertValueMessage(self, value, message, path): + """Convert a JSON representation into Value message.""" + if isinstance(value, dict): + self._ConvertStructMessage(value, message.struct_value, path) + elif isinstance(value, list): + self._ConvertListValueMessage(value, message.list_value, path) + elif value is None: + message.null_value = 0 + elif isinstance(value, bool): + message.bool_value = value + elif isinstance(value, str): + message.string_value = value + elif isinstance(value, _INT_OR_FLOAT): + message.number_value = value + else: + raise ParseError('Value {0} has unexpected type {1} at {2}'.format( + value, type(value), path)) + + def _ConvertListValueMessage(self, value, message, path): + """Convert a JSON representation into ListValue message.""" + if not isinstance(value, list): + raise ParseError('ListValue must be in [] which is {0} at {1}'.format( + value, path)) + message.ClearField('values') + for index, item in enumerate(value): + self._ConvertValueMessage(item, message.values.add(), + '{0}[{1}]'.format(path, index)) + + def _ConvertStructMessage(self, value, message, path): + """Convert a JSON representation into Struct message.""" + if not isinstance(value, dict): + raise ParseError('Struct must be in a dict which is {0} at {1}'.format( + value, path)) + # Clear will mark the struct as modified so it will be created even if + # there are no values. + message.Clear() + for key in value: + self._ConvertValueMessage(value[key], message.fields[key], + '{0}.{1}'.format(path, key)) + return + + def _ConvertWrapperMessage(self, value, message, path): + """Convert a JSON representation into Wrapper message.""" + field = message.DESCRIPTOR.fields_by_name['value'] + setattr( + message, 'value', + _ConvertScalarFieldValue(value, field, path='{0}.value'.format(path))) + + def _ConvertMapFieldValue(self, value, message, field, path): + """Convert map field value for a message map field. + + Args: + value: A JSON object to convert the map field value. + message: A protocol message to record the converted data. + field: The descriptor of the map field to be converted. + path: parent path to log parse error info. + + Raises: + ParseError: In case of convert problems. + """ + if not isinstance(value, dict): + raise ParseError( + 'Map field {0} must be in a dict which is {1} at {2}'.format( + field.name, value, path)) + key_field = field.message_type.fields_by_name['key'] + value_field = field.message_type.fields_by_name['value'] + for key in value: + key_value = _ConvertScalarFieldValue(key, key_field, + '{0}.key'.format(path), True) + if value_field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + self.ConvertMessage(value[key], + getattr(message, field.name)[key_value], + '{0}[{1}]'.format(path, key_value)) + else: + getattr(message, field.name)[key_value] = _ConvertScalarFieldValue( + value[key], value_field, path='{0}[{1}]'.format(path, key_value)) + + +def _ConvertScalarFieldValue(value, field, path, require_str=False): + """Convert a single scalar field value. + + Args: + value: A scalar value to convert the scalar field value. + field: The descriptor of the field to convert. + path: parent path to log parse error info. + require_str: If True, the field value must be a str. + + Returns: + The converted scalar field value + + Raises: + ParseError: In case of convert problems. + """ + try: + if field.cpp_type in _INT_TYPES: + return _ConvertInteger(value) + elif field.cpp_type in _FLOAT_TYPES: + return _ConvertFloat(value, field) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: + return _ConvertBool(value, require_str) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: + if field.type == descriptor.FieldDescriptor.TYPE_BYTES: + if isinstance(value, str): + encoded = value.encode('utf-8') + else: + encoded = value + # Add extra padding '=' + padded_value = encoded + b'=' * (4 - len(encoded) % 4) + return base64.urlsafe_b64decode(padded_value) + else: + # Checking for unpaired surrogates appears to be unreliable, + # depending on the specific Python version, so we check manually. + if _UNPAIRED_SURROGATE_PATTERN.search(value): + raise ParseError('Unpaired surrogate') + return value + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: + # Convert an enum value. + enum_value = field.enum_type.values_by_name.get(value, None) + if enum_value is None: + try: + number = int(value) + enum_value = field.enum_type.values_by_number.get(number, None) + except ValueError: + raise ParseError('Invalid enum value {0} for enum type {1}'.format( + value, field.enum_type.full_name)) + if enum_value is None: + if field.file.syntax == 'proto3': + # Proto3 accepts unknown enums. + return number + raise ParseError('Invalid enum value {0} for enum type {1}'.format( + value, field.enum_type.full_name)) + return enum_value.number + except ParseError as e: + raise ParseError('{0} at {1}'.format(e, path)) + + +def _ConvertInteger(value): + """Convert an integer. + + Args: + value: A scalar value to convert. + + Returns: + The integer value. + + Raises: + ParseError: If an integer couldn't be consumed. + """ + if isinstance(value, float) and not value.is_integer(): + raise ParseError('Couldn\'t parse integer: {0}'.format(value)) + + if isinstance(value, str) and value.find(' ') != -1: + raise ParseError('Couldn\'t parse integer: "{0}"'.format(value)) + + if isinstance(value, bool): + raise ParseError('Bool value {0} is not acceptable for ' + 'integer field'.format(value)) + + return int(value) + + +def _ConvertFloat(value, field): + """Convert an floating point number.""" + if isinstance(value, float): + if math.isnan(value): + raise ParseError('Couldn\'t parse NaN, use quoted "NaN" instead') + if math.isinf(value): + if value > 0: + raise ParseError('Couldn\'t parse Infinity or value too large, ' + 'use quoted "Infinity" instead') + else: + raise ParseError('Couldn\'t parse -Infinity or value too small, ' + 'use quoted "-Infinity" instead') + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: + # pylint: disable=protected-access + if value > type_checkers._FLOAT_MAX: + raise ParseError('Float value too large') + # pylint: disable=protected-access + if value < type_checkers._FLOAT_MIN: + raise ParseError('Float value too small') + if value == 'nan': + raise ParseError('Couldn\'t parse float "nan", use "NaN" instead') + try: + # Assume Python compatible syntax. + return float(value) + except ValueError: + # Check alternative spellings. + if value == _NEG_INFINITY: + return float('-inf') + elif value == _INFINITY: + return float('inf') + elif value == _NAN: + return float('nan') + else: + raise ParseError('Couldn\'t parse float: {0}'.format(value)) + + +def _ConvertBool(value, require_str): + """Convert a boolean value. + + Args: + value: A scalar value to convert. + require_str: If True, value must be a str. + + Returns: + The bool parsed. + + Raises: + ParseError: If a boolean value couldn't be consumed. + """ + if require_str: + if value == 'true': + return True + elif value == 'false': + return False + else: + raise ParseError('Expected "true" or "false", not {0}'.format(value)) + + if not isinstance(value, bool): + raise ParseError('Expected true or false without quotes') + return value + +_WKTJSONMETHODS = { + 'google.protobuf.Any': ['_AnyMessageToJsonObject', + '_ConvertAnyMessage'], + 'google.protobuf.Duration': ['_GenericMessageToJsonObject', + '_ConvertGenericMessage'], + 'google.protobuf.FieldMask': ['_GenericMessageToJsonObject', + '_ConvertGenericMessage'], + 'google.protobuf.ListValue': ['_ListValueMessageToJsonObject', + '_ConvertListValueMessage'], + 'google.protobuf.Struct': ['_StructMessageToJsonObject', + '_ConvertStructMessage'], + 'google.protobuf.Timestamp': ['_GenericMessageToJsonObject', + '_ConvertGenericMessage'], + 'google.protobuf.Value': ['_ValueMessageToJsonObject', + '_ConvertValueMessage'] +} diff --git a/lib/protobuf/message.py b/lib/protobuf/message.py new file mode 100644 index 0000000..76c6802 --- /dev/null +++ b/lib/protobuf/message.py @@ -0,0 +1,424 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# TODO(robinson): We should just make these methods all "pure-virtual" and move +# all implementation out, into reflection.py for now. + + +"""Contains an abstract base class for protocol messages.""" + +__author__ = 'robinson@google.com (Will Robinson)' + +class Error(Exception): + """Base error type for this module.""" + pass + + +class DecodeError(Error): + """Exception raised when deserializing messages.""" + pass + + +class EncodeError(Error): + """Exception raised when serializing messages.""" + pass + + +class Message(object): + + """Abstract base class for protocol messages. + + Protocol message classes are almost always generated by the protocol + compiler. These generated types subclass Message and implement the methods + shown below. + """ + + # TODO(robinson): Link to an HTML document here. + + # TODO(robinson): Document that instances of this class will also + # have an Extensions attribute with __getitem__ and __setitem__. + # Again, not sure how to best convey this. + + # TODO(robinson): Document that the class must also have a static + # RegisterExtension(extension_field) method. + # Not sure how to best express at this point. + + # TODO(robinson): Document these fields and methods. + + __slots__ = [] + + #: The :class:`google.protobuf.descriptor.Descriptor` for this message type. + DESCRIPTOR = None + + def __deepcopy__(self, memo=None): + clone = type(self)() + clone.MergeFrom(self) + return clone + + def __eq__(self, other_msg): + """Recursively compares two messages by value and structure.""" + raise NotImplementedError + + def __ne__(self, other_msg): + # Can't just say self != other_msg, since that would infinitely recurse. :) + return not self == other_msg + + def __hash__(self): + raise TypeError('unhashable object') + + def __str__(self): + """Outputs a human-readable representation of the message.""" + raise NotImplementedError + + def __unicode__(self): + """Outputs a human-readable representation of the message.""" + raise NotImplementedError + + def MergeFrom(self, other_msg): + """Merges the contents of the specified message into current message. + + This method merges the contents of the specified message into the current + message. Singular fields that are set in the specified message overwrite + the corresponding fields in the current message. Repeated fields are + appended. Singular sub-messages and groups are recursively merged. + + Args: + other_msg (Message): A message to merge into the current message. + """ + raise NotImplementedError + + def CopyFrom(self, other_msg): + """Copies the content of the specified message into the current message. + + The method clears the current message and then merges the specified + message using MergeFrom. + + Args: + other_msg (Message): A message to copy into the current one. + """ + if self is other_msg: + return + self.Clear() + self.MergeFrom(other_msg) + + def Clear(self): + """Clears all data that was set in the message.""" + raise NotImplementedError + + def SetInParent(self): + """Mark this as present in the parent. + + This normally happens automatically when you assign a field of a + sub-message, but sometimes you want to make the sub-message + present while keeping it empty. If you find yourself using this, + you may want to reconsider your design. + """ + raise NotImplementedError + + def IsInitialized(self): + """Checks if the message is initialized. + + Returns: + bool: The method returns True if the message is initialized (i.e. all of + its required fields are set). + """ + raise NotImplementedError + + # TODO(robinson): MergeFromString() should probably return None and be + # implemented in terms of a helper that returns the # of bytes read. Our + # deserialization routines would use the helper when recursively + # deserializing, but the end user would almost always just want the no-return + # MergeFromString(). + + def MergeFromString(self, serialized): + """Merges serialized protocol buffer data into this message. + + When we find a field in `serialized` that is already present + in this message: + + - If it's a "repeated" field, we append to the end of our list. + - Else, if it's a scalar, we overwrite our field. + - Else, (it's a nonrepeated composite), we recursively merge + into the existing composite. + + Args: + serialized (bytes): Any object that allows us to call + ``memoryview(serialized)`` to access a string of bytes using the + buffer interface. + + Returns: + int: The number of bytes read from `serialized`. + For non-group messages, this will always be `len(serialized)`, + but for messages which are actually groups, this will + generally be less than `len(serialized)`, since we must + stop when we reach an ``END_GROUP`` tag. Note that if + we *do* stop because of an ``END_GROUP`` tag, the number + of bytes returned does not include the bytes + for the ``END_GROUP`` tag information. + + Raises: + DecodeError: if the input cannot be parsed. + """ + # TODO(robinson): Document handling of unknown fields. + # TODO(robinson): When we switch to a helper, this will return None. + raise NotImplementedError + + def ParseFromString(self, serialized): + """Parse serialized protocol buffer data into this message. + + Like :func:`MergeFromString()`, except we clear the object first. + + Raises: + message.DecodeError if the input cannot be parsed. + """ + self.Clear() + return self.MergeFromString(serialized) + + def SerializeToString(self, **kwargs): + """Serializes the protocol message to a binary string. + + Keyword Args: + deterministic (bool): If true, requests deterministic serialization + of the protobuf, with predictable ordering of map keys. + + Returns: + A binary string representation of the message if all of the required + fields in the message are set (i.e. the message is initialized). + + Raises: + EncodeError: if the message isn't initialized (see :func:`IsInitialized`). + """ + raise NotImplementedError + + def SerializePartialToString(self, **kwargs): + """Serializes the protocol message to a binary string. + + This method is similar to SerializeToString but doesn't check if the + message is initialized. + + Keyword Args: + deterministic (bool): If true, requests deterministic serialization + of the protobuf, with predictable ordering of map keys. + + Returns: + bytes: A serialized representation of the partial message. + """ + raise NotImplementedError + + # TODO(robinson): Decide whether we like these better + # than auto-generated has_foo() and clear_foo() methods + # on the instances themselves. This way is less consistent + # with C++, but it makes reflection-type access easier and + # reduces the number of magically autogenerated things. + # + # TODO(robinson): Be sure to document (and test) exactly + # which field names are accepted here. Are we case-sensitive? + # What do we do with fields that share names with Python keywords + # like 'lambda' and 'yield'? + # + # nnorwitz says: + # """ + # Typically (in python), an underscore is appended to names that are + # keywords. So they would become lambda_ or yield_. + # """ + def ListFields(self): + """Returns a list of (FieldDescriptor, value) tuples for present fields. + + A message field is non-empty if HasField() would return true. A singular + primitive field is non-empty if HasField() would return true in proto2 or it + is non zero in proto3. A repeated field is non-empty if it contains at least + one element. The fields are ordered by field number. + + Returns: + list[tuple(FieldDescriptor, value)]: field descriptors and values + for all fields in the message which are not empty. The values vary by + field type. + """ + raise NotImplementedError + + def HasField(self, field_name): + """Checks if a certain field is set for the message. + + For a oneof group, checks if any field inside is set. Note that if the + field_name is not defined in the message descriptor, :exc:`ValueError` will + be raised. + + Args: + field_name (str): The name of the field to check for presence. + + Returns: + bool: Whether a value has been set for the named field. + + Raises: + ValueError: if the `field_name` is not a member of this message. + """ + raise NotImplementedError + + def ClearField(self, field_name): + """Clears the contents of a given field. + + Inside a oneof group, clears the field set. If the name neither refers to a + defined field or oneof group, :exc:`ValueError` is raised. + + Args: + field_name (str): The name of the field to check for presence. + + Raises: + ValueError: if the `field_name` is not a member of this message. + """ + raise NotImplementedError + + def WhichOneof(self, oneof_group): + """Returns the name of the field that is set inside a oneof group. + + If no field is set, returns None. + + Args: + oneof_group (str): the name of the oneof group to check. + + Returns: + str or None: The name of the group that is set, or None. + + Raises: + ValueError: no group with the given name exists + """ + raise NotImplementedError + + def HasExtension(self, extension_handle): + """Checks if a certain extension is present for this message. + + Extensions are retrieved using the :attr:`Extensions` mapping (if present). + + Args: + extension_handle: The handle for the extension to check. + + Returns: + bool: Whether the extension is present for this message. + + Raises: + KeyError: if the extension is repeated. Similar to repeated fields, + there is no separate notion of presence: a "not present" repeated + extension is an empty list. + """ + raise NotImplementedError + + def ClearExtension(self, extension_handle): + """Clears the contents of a given extension. + + Args: + extension_handle: The handle for the extension to clear. + """ + raise NotImplementedError + + def UnknownFields(self): + """Returns the UnknownFieldSet. + + Returns: + UnknownFieldSet: The unknown fields stored in this message. + """ + raise NotImplementedError + + def DiscardUnknownFields(self): + """Clears all fields in the :class:`UnknownFieldSet`. + + This operation is recursive for nested message. + """ + raise NotImplementedError + + def ByteSize(self): + """Returns the serialized size of this message. + + Recursively calls ByteSize() on all contained messages. + + Returns: + int: The number of bytes required to serialize this message. + """ + raise NotImplementedError + + @classmethod + def FromString(cls, s): + raise NotImplementedError + + @staticmethod + def RegisterExtension(extension_handle): + raise NotImplementedError + + def _SetListener(self, message_listener): + """Internal method used by the protocol message implementation. + Clients should not call this directly. + + Sets a listener that this message will call on certain state transitions. + + The purpose of this method is to register back-edges from children to + parents at runtime, for the purpose of setting "has" bits and + byte-size-dirty bits in the parent and ancestor objects whenever a child or + descendant object is modified. + + If the client wants to disconnect this Message from the object tree, she + explicitly sets callback to None. + + If message_listener is None, unregisters any existing listener. Otherwise, + message_listener must implement the MessageListener interface in + internal/message_listener.py, and we discard any listener registered + via a previous _SetListener() call. + """ + raise NotImplementedError + + def __getstate__(self): + """Support the pickle protocol.""" + return dict(serialized=self.SerializePartialToString()) + + def __setstate__(self, state): + """Support the pickle protocol.""" + self.__init__() + serialized = state['serialized'] + # On Python 3, using encoding='latin1' is required for unpickling + # protos pickled by Python 2. + if not isinstance(serialized, bytes): + serialized = serialized.encode('latin1') + self.ParseFromString(serialized) + + def __reduce__(self): + message_descriptor = self.DESCRIPTOR + if message_descriptor.containing_type is None: + return type(self), (), self.__getstate__() + # the message type must be nested. + # Python does not pickle nested classes; use the symbol_database on the + # receiving end. + container = message_descriptor + return (_InternalConstructMessage, (container.full_name,), + self.__getstate__()) + + +def _InternalConstructMessage(full_name): + """Constructs a nested message.""" + from google.protobuf import symbol_database # pylint:disable=g-import-not-at-top + + return symbol_database.Default().GetSymbol(full_name)() diff --git a/lib/protobuf/message_factory.py b/lib/protobuf/message_factory.py new file mode 100644 index 0000000..3656fa6 --- /dev/null +++ b/lib/protobuf/message_factory.py @@ -0,0 +1,185 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Provides a factory class for generating dynamic messages. + +The easiest way to use this class is if you have access to the FileDescriptor +protos containing the messages you want to create you can just do the following: + +message_classes = message_factory.GetMessages(iterable_of_file_descriptors) +my_proto_instance = message_classes['some.proto.package.MessageName']() +""" + +__author__ = 'matthewtoia@google.com (Matt Toia)' + +from google.protobuf.internal import api_implementation +from google.protobuf import descriptor_pool +from google.protobuf import message + +if api_implementation.Type() == 'cpp': + from google.protobuf.pyext import cpp_message as message_impl +else: + from google.protobuf.internal import python_message as message_impl + + +# The type of all Message classes. +_GENERATED_PROTOCOL_MESSAGE_TYPE = message_impl.GeneratedProtocolMessageType + + +class MessageFactory(object): + """Factory for creating Proto2 messages from descriptors in a pool.""" + + def __init__(self, pool=None): + """Initializes a new factory.""" + self.pool = pool or descriptor_pool.DescriptorPool() + + # local cache of all classes built from protobuf descriptors + self._classes = {} + + def GetPrototype(self, descriptor): + """Obtains a proto2 message class based on the passed in descriptor. + + Passing a descriptor with a fully qualified name matching a previous + invocation will cause the same class to be returned. + + Args: + descriptor: The descriptor to build from. + + Returns: + A class describing the passed in descriptor. + """ + if descriptor not in self._classes: + result_class = self.CreatePrototype(descriptor) + # The assignment to _classes is redundant for the base implementation, but + # might avoid confusion in cases where CreatePrototype gets overridden and + # does not call the base implementation. + self._classes[descriptor] = result_class + return result_class + return self._classes[descriptor] + + def CreatePrototype(self, descriptor): + """Builds a proto2 message class based on the passed in descriptor. + + Don't call this function directly, it always creates a new class. Call + GetPrototype() instead. This method is meant to be overridden in subblasses + to perform additional operations on the newly constructed class. + + Args: + descriptor: The descriptor to build from. + + Returns: + A class describing the passed in descriptor. + """ + descriptor_name = descriptor.name + result_class = _GENERATED_PROTOCOL_MESSAGE_TYPE( + descriptor_name, + (message.Message,), + { + 'DESCRIPTOR': descriptor, + # If module not set, it wrongly points to message_factory module. + '__module__': None, + }) + result_class._FACTORY = self # pylint: disable=protected-access + # Assign in _classes before doing recursive calls to avoid infinite + # recursion. + self._classes[descriptor] = result_class + for field in descriptor.fields: + if field.message_type: + self.GetPrototype(field.message_type) + for extension in result_class.DESCRIPTOR.extensions: + if extension.containing_type not in self._classes: + self.GetPrototype(extension.containing_type) + extended_class = self._classes[extension.containing_type] + extended_class.RegisterExtension(extension) + return result_class + + def GetMessages(self, files): + """Gets all the messages from a specified file. + + This will find and resolve dependencies, failing if the descriptor + pool cannot satisfy them. + + Args: + files: The file names to extract messages from. + + Returns: + A dictionary mapping proto names to the message classes. This will include + any dependent messages as well as any messages defined in the same file as + a specified message. + """ + result = {} + for file_name in files: + file_desc = self.pool.FindFileByName(file_name) + for desc in file_desc.message_types_by_name.values(): + result[desc.full_name] = self.GetPrototype(desc) + + # While the extension FieldDescriptors are created by the descriptor pool, + # the python classes created in the factory need them to be registered + # explicitly, which is done below. + # + # The call to RegisterExtension will specifically check if the + # extension was already registered on the object and either + # ignore the registration if the original was the same, or raise + # an error if they were different. + + for extension in file_desc.extensions_by_name.values(): + if extension.containing_type not in self._classes: + self.GetPrototype(extension.containing_type) + extended_class = self._classes[extension.containing_type] + extended_class.RegisterExtension(extension) + return result + + +_FACTORY = MessageFactory() + + +def GetMessages(file_protos): + """Builds a dictionary of all the messages available in a set of files. + + Args: + file_protos: Iterable of FileDescriptorProto to build messages out of. + + Returns: + A dictionary mapping proto names to the message classes. This will include + any dependent messages as well as any messages defined in the same file as + a specified message. + """ + # The cpp implementation of the protocol buffer library requires to add the + # message in topological order of the dependency graph. + file_by_name = {file_proto.name: file_proto for file_proto in file_protos} + def _AddFile(file_proto): + for dependency in file_proto.dependency: + if dependency in file_by_name: + # Remove from elements to be visited, in order to cut cycles. + _AddFile(file_by_name.pop(dependency)) + _FACTORY.pool.Add(file_proto) + while file_by_name: + _AddFile(file_by_name.popitem()[1]) + return _FACTORY.GetMessages([file_proto.name for file_proto in file_protos]) diff --git a/lib/protobuf/proto_builder.py b/lib/protobuf/proto_builder.py new file mode 100644 index 0000000..a4667ce --- /dev/null +++ b/lib/protobuf/proto_builder.py @@ -0,0 +1,134 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Dynamic Protobuf class creator.""" + +from collections import OrderedDict +import hashlib +import os + +from google.protobuf import descriptor_pb2 +from google.protobuf import descriptor +from google.protobuf import message_factory + + +def _GetMessageFromFactory(factory, full_name): + """Get a proto class from the MessageFactory by name. + + Args: + factory: a MessageFactory instance. + full_name: str, the fully qualified name of the proto type. + Returns: + A class, for the type identified by full_name. + Raises: + KeyError, if the proto is not found in the factory's descriptor pool. + """ + proto_descriptor = factory.pool.FindMessageTypeByName(full_name) + proto_cls = factory.GetPrototype(proto_descriptor) + return proto_cls + + +def MakeSimpleProtoClass(fields, full_name=None, pool=None): + """Create a Protobuf class whose fields are basic types. + + Note: this doesn't validate field names! + + Args: + fields: dict of {name: field_type} mappings for each field in the proto. If + this is an OrderedDict the order will be maintained, otherwise the + fields will be sorted by name. + full_name: optional str, the fully-qualified name of the proto type. + pool: optional DescriptorPool instance. + Returns: + a class, the new protobuf class with a FileDescriptor. + """ + factory = message_factory.MessageFactory(pool=pool) + + if full_name is not None: + try: + proto_cls = _GetMessageFromFactory(factory, full_name) + return proto_cls + except KeyError: + # The factory's DescriptorPool doesn't know about this class yet. + pass + + # Get a list of (name, field_type) tuples from the fields dict. If fields was + # an OrderedDict we keep the order, but otherwise we sort the field to ensure + # consistent ordering. + field_items = fields.items() + if not isinstance(fields, OrderedDict): + field_items = sorted(field_items) + + # Use a consistent file name that is unlikely to conflict with any imported + # proto files. + fields_hash = hashlib.sha1() + for f_name, f_type in field_items: + fields_hash.update(f_name.encode('utf-8')) + fields_hash.update(str(f_type).encode('utf-8')) + proto_file_name = fields_hash.hexdigest() + '.proto' + + # If the proto is anonymous, use the same hash to name it. + if full_name is None: + full_name = ('net.proto2.python.public.proto_builder.AnonymousProto_' + + fields_hash.hexdigest()) + try: + proto_cls = _GetMessageFromFactory(factory, full_name) + return proto_cls + except KeyError: + # The factory's DescriptorPool doesn't know about this class yet. + pass + + # This is the first time we see this proto: add a new descriptor to the pool. + factory.pool.Add( + _MakeFileDescriptorProto(proto_file_name, full_name, field_items)) + return _GetMessageFromFactory(factory, full_name) + + +def _MakeFileDescriptorProto(proto_file_name, full_name, field_items): + """Populate FileDescriptorProto for MessageFactory's DescriptorPool.""" + package, name = full_name.rsplit('.', 1) + file_proto = descriptor_pb2.FileDescriptorProto() + file_proto.name = os.path.join(package.replace('.', '/'), proto_file_name) + file_proto.package = package + desc_proto = file_proto.message_type.add() + desc_proto.name = name + for f_number, (f_name, f_type) in enumerate(field_items, 1): + field_proto = desc_proto.field.add() + field_proto.name = f_name + # # If the number falls in the reserved range, reassign it to the correct + # # number after the range. + if f_number >= descriptor.FieldDescriptor.FIRST_RESERVED_FIELD_NUMBER: + f_number += ( + descriptor.FieldDescriptor.LAST_RESERVED_FIELD_NUMBER - + descriptor.FieldDescriptor.FIRST_RESERVED_FIELD_NUMBER + 1) + field_proto.number = f_number + field_proto.label = descriptor_pb2.FieldDescriptorProto.LABEL_OPTIONAL + field_proto.type = f_type + return file_proto diff --git a/lib/protobuf/pyext/__init__.py b/lib/protobuf/pyext/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/protobuf/pyext/_message.cpython-310-x86_64-linux-gnu.so b/lib/protobuf/pyext/_message.cpython-310-x86_64-linux-gnu.so new file mode 100755 index 0000000..6e81155 Binary files /dev/null and b/lib/protobuf/pyext/_message.cpython-310-x86_64-linux-gnu.so differ diff --git a/lib/protobuf/pyext/cpp_message.py b/lib/protobuf/pyext/cpp_message.py new file mode 100644 index 0000000..fc8eb32 --- /dev/null +++ b/lib/protobuf/pyext/cpp_message.py @@ -0,0 +1,65 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Protocol message implementation hooks for C++ implementation. + +Contains helper functions used to create protocol message classes from +Descriptor objects at runtime backed by the protocol buffer C++ API. +""" + +__author__ = 'tibell@google.com (Johan Tibell)' + +from google.protobuf.pyext import _message + + +class GeneratedProtocolMessageType(_message.MessageMeta): + + """Metaclass for protocol message classes created at runtime from Descriptors. + + The protocol compiler currently uses this metaclass to create protocol + message classes at runtime. Clients can also manually create their own + classes at runtime, as in this example: + + mydescriptor = Descriptor(.....) + factory = symbol_database.Default() + factory.pool.AddDescriptor(mydescriptor) + MyProtoClass = factory.GetPrototype(mydescriptor) + myproto_instance = MyProtoClass() + myproto.foo_field = 23 + ... + + The above example will not work for nested types. If you wish to include them, + use reflection.MakeClass() instead of manually instantiating the class in + order to create the appropriate class structure. + """ + + # Must be consistent with the protocol-compiler code in + # proto2/compiler/internal/generator.*. + _DESCRIPTOR_KEY = 'DESCRIPTOR' diff --git a/lib/protobuf/reflection.py b/lib/protobuf/reflection.py new file mode 100644 index 0000000..81e1885 --- /dev/null +++ b/lib/protobuf/reflection.py @@ -0,0 +1,95 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +# This code is meant to work on Python 2.4 and above only. + +"""Contains a metaclass and helper functions used to create +protocol message classes from Descriptor objects at runtime. + +Recall that a metaclass is the "type" of a class. +(A class is to a metaclass what an instance is to a class.) + +In this case, we use the GeneratedProtocolMessageType metaclass +to inject all the useful functionality into the classes +output by the protocol compiler at compile-time. + +The upshot of all this is that the real implementation +details for ALL pure-Python protocol buffers are *here in +this file*. +""" + +__author__ = 'robinson@google.com (Will Robinson)' + + +from google.protobuf import message_factory +from google.protobuf import symbol_database + +# The type of all Message classes. +# Part of the public interface, but normally only used by message factories. +GeneratedProtocolMessageType = message_factory._GENERATED_PROTOCOL_MESSAGE_TYPE + +MESSAGE_CLASS_CACHE = {} + + +# Deprecated. Please NEVER use reflection.ParseMessage(). +def ParseMessage(descriptor, byte_str): + """Generate a new Message instance from this Descriptor and a byte string. + + DEPRECATED: ParseMessage is deprecated because it is using MakeClass(). + Please use MessageFactory.GetPrototype() instead. + + Args: + descriptor: Protobuf Descriptor object + byte_str: Serialized protocol buffer byte string + + Returns: + Newly created protobuf Message object. + """ + result_class = MakeClass(descriptor) + new_msg = result_class() + new_msg.ParseFromString(byte_str) + return new_msg + + +# Deprecated. Please NEVER use reflection.MakeClass(). +def MakeClass(descriptor): + """Construct a class object for a protobuf described by descriptor. + + DEPRECATED: use MessageFactory.GetPrototype() instead. + + Args: + descriptor: A descriptor.Descriptor object describing the protobuf. + Returns: + The Message class object described by the descriptor. + """ + # Original implementation leads to duplicate message classes, which won't play + # well with extensions. Message factory info is also missing. + # Redirect to message_factory. + return symbol_database.Default().GetPrototype(descriptor) diff --git a/lib/protobuf/service.py b/lib/protobuf/service.py new file mode 100644 index 0000000..5625246 --- /dev/null +++ b/lib/protobuf/service.py @@ -0,0 +1,228 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""DEPRECATED: Declares the RPC service interfaces. + +This module declares the abstract interfaces underlying proto2 RPC +services. These are intended to be independent of any particular RPC +implementation, so that proto2 services can be used on top of a variety +of implementations. Starting with version 2.3.0, RPC implementations should +not try to build on these, but should instead provide code generator plugins +which generate code specific to the particular RPC implementation. This way +the generated code can be more appropriate for the implementation in use +and can avoid unnecessary layers of indirection. +""" + +__author__ = 'petar@google.com (Petar Petrov)' + + +class RpcException(Exception): + """Exception raised on failed blocking RPC method call.""" + pass + + +class Service(object): + + """Abstract base interface for protocol-buffer-based RPC services. + + Services themselves are abstract classes (implemented either by servers or as + stubs), but they subclass this base interface. The methods of this + interface can be used to call the methods of the service without knowing + its exact type at compile time (analogous to the Message interface). + """ + + def GetDescriptor(): + """Retrieves this service's descriptor.""" + raise NotImplementedError + + def CallMethod(self, method_descriptor, rpc_controller, + request, done): + """Calls a method of the service specified by method_descriptor. + + If "done" is None then the call is blocking and the response + message will be returned directly. Otherwise the call is asynchronous + and "done" will later be called with the response value. + + In the blocking case, RpcException will be raised on error. + + Preconditions: + + * method_descriptor.service == GetDescriptor + * request is of the exact same classes as returned by + GetRequestClass(method). + * After the call has started, the request must not be modified. + * "rpc_controller" is of the correct type for the RPC implementation being + used by this Service. For stubs, the "correct type" depends on the + RpcChannel which the stub is using. + + Postconditions: + + * "done" will be called when the method is complete. This may be + before CallMethod() returns or it may be at some point in the future. + * If the RPC failed, the response value passed to "done" will be None. + Further details about the failure can be found by querying the + RpcController. + """ + raise NotImplementedError + + def GetRequestClass(self, method_descriptor): + """Returns the class of the request message for the specified method. + + CallMethod() requires that the request is of a particular subclass of + Message. GetRequestClass() gets the default instance of this required + type. + + Example: + method = service.GetDescriptor().FindMethodByName("Foo") + request = stub.GetRequestClass(method)() + request.ParseFromString(input) + service.CallMethod(method, request, callback) + """ + raise NotImplementedError + + def GetResponseClass(self, method_descriptor): + """Returns the class of the response message for the specified method. + + This method isn't really needed, as the RpcChannel's CallMethod constructs + the response protocol message. It's provided anyway in case it is useful + for the caller to know the response type in advance. + """ + raise NotImplementedError + + +class RpcController(object): + + """An RpcController mediates a single method call. + + The primary purpose of the controller is to provide a way to manipulate + settings specific to the RPC implementation and to find out about RPC-level + errors. The methods provided by the RpcController interface are intended + to be a "least common denominator" set of features which we expect all + implementations to support. Specific implementations may provide more + advanced features (e.g. deadline propagation). + """ + + # Client-side methods below + + def Reset(self): + """Resets the RpcController to its initial state. + + After the RpcController has been reset, it may be reused in + a new call. Must not be called while an RPC is in progress. + """ + raise NotImplementedError + + def Failed(self): + """Returns true if the call failed. + + After a call has finished, returns true if the call failed. The possible + reasons for failure depend on the RPC implementation. Failed() must not + be called before a call has finished. If Failed() returns true, the + contents of the response message are undefined. + """ + raise NotImplementedError + + def ErrorText(self): + """If Failed is true, returns a human-readable description of the error.""" + raise NotImplementedError + + def StartCancel(self): + """Initiate cancellation. + + Advises the RPC system that the caller desires that the RPC call be + canceled. The RPC system may cancel it immediately, may wait awhile and + then cancel it, or may not even cancel the call at all. If the call is + canceled, the "done" callback will still be called and the RpcController + will indicate that the call failed at that time. + """ + raise NotImplementedError + + # Server-side methods below + + def SetFailed(self, reason): + """Sets a failure reason. + + Causes Failed() to return true on the client side. "reason" will be + incorporated into the message returned by ErrorText(). If you find + you need to return machine-readable information about failures, you + should incorporate it into your response protocol buffer and should + NOT call SetFailed(). + """ + raise NotImplementedError + + def IsCanceled(self): + """Checks if the client cancelled the RPC. + + If true, indicates that the client canceled the RPC, so the server may + as well give up on replying to it. The server should still call the + final "done" callback. + """ + raise NotImplementedError + + def NotifyOnCancel(self, callback): + """Sets a callback to invoke on cancel. + + Asks that the given callback be called when the RPC is canceled. The + callback will always be called exactly once. If the RPC completes without + being canceled, the callback will be called after completion. If the RPC + has already been canceled when NotifyOnCancel() is called, the callback + will be called immediately. + + NotifyOnCancel() must be called no more than once per request. + """ + raise NotImplementedError + + +class RpcChannel(object): + + """Abstract interface for an RPC channel. + + An RpcChannel represents a communication line to a service which can be used + to call that service's methods. The service may be running on another + machine. Normally, you should not use an RpcChannel directly, but instead + construct a stub {@link Service} wrapping it. Example: + + Example: + RpcChannel channel = rpcImpl.Channel("remotehost.example.com:1234") + RpcController controller = rpcImpl.Controller() + MyService service = MyService_Stub(channel) + service.MyMethod(controller, request, callback) + """ + + def CallMethod(self, method_descriptor, rpc_controller, + request, response_class, done): + """Calls the method identified by the descriptor. + + Call the given method of the remote service. The signature of this + procedure looks the same as Service.CallMethod(), but the requirements + are less strict in one important way: the request object doesn't have to + be of any specific class as long as its descriptor is method.input_type. + """ + raise NotImplementedError diff --git a/lib/protobuf/service_reflection.py b/lib/protobuf/service_reflection.py new file mode 100644 index 0000000..f82ab71 --- /dev/null +++ b/lib/protobuf/service_reflection.py @@ -0,0 +1,295 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Contains metaclasses used to create protocol service and service stub +classes from ServiceDescriptor objects at runtime. + +The GeneratedServiceType and GeneratedServiceStubType metaclasses are used to +inject all useful functionality into the classes output by the protocol +compiler at compile-time. +""" + +__author__ = 'petar@google.com (Petar Petrov)' + + +class GeneratedServiceType(type): + + """Metaclass for service classes created at runtime from ServiceDescriptors. + + Implementations for all methods described in the Service class are added here + by this class. We also create properties to allow getting/setting all fields + in the protocol message. + + The protocol compiler currently uses this metaclass to create protocol service + classes at runtime. Clients can also manually create their own classes at + runtime, as in this example:: + + mydescriptor = ServiceDescriptor(.....) + class MyProtoService(service.Service): + __metaclass__ = GeneratedServiceType + DESCRIPTOR = mydescriptor + myservice_instance = MyProtoService() + # ... + """ + + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __init__(cls, name, bases, dictionary): + """Creates a message service class. + + Args: + name: Name of the class (ignored, but required by the metaclass + protocol). + bases: Base classes of the class being constructed. + dictionary: The class dictionary of the class being constructed. + dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object + describing this protocol service type. + """ + # Don't do anything if this class doesn't have a descriptor. This happens + # when a service class is subclassed. + if GeneratedServiceType._DESCRIPTOR_KEY not in dictionary: + return + + descriptor = dictionary[GeneratedServiceType._DESCRIPTOR_KEY] + service_builder = _ServiceBuilder(descriptor) + service_builder.BuildService(cls) + cls.DESCRIPTOR = descriptor + + +class GeneratedServiceStubType(GeneratedServiceType): + + """Metaclass for service stubs created at runtime from ServiceDescriptors. + + This class has similar responsibilities as GeneratedServiceType, except that + it creates the service stub classes. + """ + + _DESCRIPTOR_KEY = 'DESCRIPTOR' + + def __init__(cls, name, bases, dictionary): + """Creates a message service stub class. + + Args: + name: Name of the class (ignored, here). + bases: Base classes of the class being constructed. + dictionary: The class dictionary of the class being constructed. + dictionary[_DESCRIPTOR_KEY] must contain a ServiceDescriptor object + describing this protocol service type. + """ + super(GeneratedServiceStubType, cls).__init__(name, bases, dictionary) + # Don't do anything if this class doesn't have a descriptor. This happens + # when a service stub is subclassed. + if GeneratedServiceStubType._DESCRIPTOR_KEY not in dictionary: + return + + descriptor = dictionary[GeneratedServiceStubType._DESCRIPTOR_KEY] + service_stub_builder = _ServiceStubBuilder(descriptor) + service_stub_builder.BuildServiceStub(cls) + + +class _ServiceBuilder(object): + + """This class constructs a protocol service class using a service descriptor. + + Given a service descriptor, this class constructs a class that represents + the specified service descriptor. One service builder instance constructs + exactly one service class. That means all instances of that class share the + same builder. + """ + + def __init__(self, service_descriptor): + """Initializes an instance of the service class builder. + + Args: + service_descriptor: ServiceDescriptor to use when constructing the + service class. + """ + self.descriptor = service_descriptor + + def BuildService(builder, cls): + """Constructs the service class. + + Args: + cls: The class that will be constructed. + """ + + # CallMethod needs to operate with an instance of the Service class. This + # internal wrapper function exists only to be able to pass the service + # instance to the method that does the real CallMethod work. + # Making sure to use exact argument names from the abstract interface in + # service.py to match the type signature + def _WrapCallMethod(self, method_descriptor, rpc_controller, request, done): + return builder._CallMethod(self, method_descriptor, rpc_controller, + request, done) + + def _WrapGetRequestClass(self, method_descriptor): + return builder._GetRequestClass(method_descriptor) + + def _WrapGetResponseClass(self, method_descriptor): + return builder._GetResponseClass(method_descriptor) + + builder.cls = cls + cls.CallMethod = _WrapCallMethod + cls.GetDescriptor = staticmethod(lambda: builder.descriptor) + cls.GetDescriptor.__doc__ = 'Returns the service descriptor.' + cls.GetRequestClass = _WrapGetRequestClass + cls.GetResponseClass = _WrapGetResponseClass + for method in builder.descriptor.methods: + setattr(cls, method.name, builder._GenerateNonImplementedMethod(method)) + + def _CallMethod(self, srvc, method_descriptor, + rpc_controller, request, callback): + """Calls the method described by a given method descriptor. + + Args: + srvc: Instance of the service for which this method is called. + method_descriptor: Descriptor that represent the method to call. + rpc_controller: RPC controller to use for this method's execution. + request: Request protocol message. + callback: A callback to invoke after the method has completed. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'CallMethod() given method descriptor for wrong service type.') + method = getattr(srvc, method_descriptor.name) + return method(rpc_controller, request, callback) + + def _GetRequestClass(self, method_descriptor): + """Returns the class of the request protocol message. + + Args: + method_descriptor: Descriptor of the method for which to return the + request protocol message class. + + Returns: + A class that represents the input protocol message of the specified + method. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'GetRequestClass() given method descriptor for wrong service type.') + return method_descriptor.input_type._concrete_class + + def _GetResponseClass(self, method_descriptor): + """Returns the class of the response protocol message. + + Args: + method_descriptor: Descriptor of the method for which to return the + response protocol message class. + + Returns: + A class that represents the output protocol message of the specified + method. + """ + if method_descriptor.containing_service != self.descriptor: + raise RuntimeError( + 'GetResponseClass() given method descriptor for wrong service type.') + return method_descriptor.output_type._concrete_class + + def _GenerateNonImplementedMethod(self, method): + """Generates and returns a method that can be set for a service methods. + + Args: + method: Descriptor of the service method for which a method is to be + generated. + + Returns: + A method that can be added to the service class. + """ + return lambda inst, rpc_controller, request, callback: ( + self._NonImplementedMethod(method.name, rpc_controller, callback)) + + def _NonImplementedMethod(self, method_name, rpc_controller, callback): + """The body of all methods in the generated service class. + + Args: + method_name: Name of the method being executed. + rpc_controller: RPC controller used to execute this method. + callback: A callback which will be invoked when the method finishes. + """ + rpc_controller.SetFailed('Method %s not implemented.' % method_name) + callback(None) + + +class _ServiceStubBuilder(object): + + """Constructs a protocol service stub class using a service descriptor. + + Given a service descriptor, this class constructs a suitable stub class. + A stub is just a type-safe wrapper around an RpcChannel which emulates a + local implementation of the service. + + One service stub builder instance constructs exactly one class. It means all + instances of that class share the same service stub builder. + """ + + def __init__(self, service_descriptor): + """Initializes an instance of the service stub class builder. + + Args: + service_descriptor: ServiceDescriptor to use when constructing the + stub class. + """ + self.descriptor = service_descriptor + + def BuildServiceStub(self, cls): + """Constructs the stub class. + + Args: + cls: The class that will be constructed. + """ + + def _ServiceStubInit(stub, rpc_channel): + stub.rpc_channel = rpc_channel + self.cls = cls + cls.__init__ = _ServiceStubInit + for method in self.descriptor.methods: + setattr(cls, method.name, self._GenerateStubMethod(method)) + + def _GenerateStubMethod(self, method): + return (lambda inst, rpc_controller, request, callback=None: + self._StubMethod(inst, method, rpc_controller, request, callback)) + + def _StubMethod(self, stub, method_descriptor, + rpc_controller, request, callback): + """The body of all service methods in the generated stub class. + + Args: + stub: Stub instance. + method_descriptor: Descriptor of the invoked method. + rpc_controller: Rpc controller to execute the method. + request: Request protocol message. + callback: A callback to execute when the method finishes. + Returns: + Response message (in case of blocking call). + """ + return stub.rpc_channel.CallMethod( + method_descriptor, rpc_controller, request, + method_descriptor.output_type._concrete_class, callback) diff --git a/lib/protobuf/source_context_pb2.py b/lib/protobuf/source_context_pb2.py new file mode 100644 index 0000000..30cca2e --- /dev/null +++ b/lib/protobuf/source_context_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/source_context.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$google/protobuf/source_context.proto\x12\x0fgoogle.protobuf\"\"\n\rSourceContext\x12\x11\n\tfile_name\x18\x01 \x01(\tB\x8a\x01\n\x13\x63om.google.protobufB\x12SourceContextProtoP\x01Z6google.golang.org/protobuf/types/known/sourcecontextpb\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.source_context_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\023com.google.protobufB\022SourceContextProtoP\001Z6google.golang.org/protobuf/types/known/sourcecontextpb\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _SOURCECONTEXT._serialized_start=57 + _SOURCECONTEXT._serialized_end=91 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/struct_pb2.py b/lib/protobuf/struct_pb2.py new file mode 100644 index 0000000..149728c --- /dev/null +++ b/lib/protobuf/struct_pb2.py @@ -0,0 +1,36 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/struct.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\x1cgoogle/protobuf/struct.proto\x12\x0fgoogle.protobuf\"\x84\x01\n\x06Struct\x12\x33\n\x06\x66ields\x18\x01 \x03(\x0b\x32#.google.protobuf.Struct.FieldsEntry\x1a\x45\n\x0b\x46ieldsEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12%\n\x05value\x18\x02 \x01(\x0b\x32\x16.google.protobuf.Value:\x02\x38\x01\"\xea\x01\n\x05Value\x12\x30\n\nnull_value\x18\x01 \x01(\x0e\x32\x1a.google.protobuf.NullValueH\x00\x12\x16\n\x0cnumber_value\x18\x02 \x01(\x01H\x00\x12\x16\n\x0cstring_value\x18\x03 \x01(\tH\x00\x12\x14\n\nbool_value\x18\x04 \x01(\x08H\x00\x12/\n\x0cstruct_value\x18\x05 \x01(\x0b\x32\x17.google.protobuf.StructH\x00\x12\x30\n\nlist_value\x18\x06 \x01(\x0b\x32\x1a.google.protobuf.ListValueH\x00\x42\x06\n\x04kind\"3\n\tListValue\x12&\n\x06values\x18\x01 \x03(\x0b\x32\x16.google.protobuf.Value*\x1b\n\tNullValue\x12\x0e\n\nNULL_VALUE\x10\x00\x42\x7f\n\x13\x63om.google.protobufB\x0bStructProtoP\x01Z/google.golang.org/protobuf/types/known/structpb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.struct_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\023com.google.protobufB\013StructProtoP\001Z/google.golang.org/protobuf/types/known/structpb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _STRUCT_FIELDSENTRY._options = None + _STRUCT_FIELDSENTRY._serialized_options = b'8\001' + _NULLVALUE._serialized_start=474 + _NULLVALUE._serialized_end=501 + _STRUCT._serialized_start=50 + _STRUCT._serialized_end=182 + _STRUCT_FIELDSENTRY._serialized_start=113 + _STRUCT_FIELDSENTRY._serialized_end=182 + _VALUE._serialized_start=185 + _VALUE._serialized_end=419 + _LISTVALUE._serialized_start=421 + _LISTVALUE._serialized_end=472 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/symbol_database.py b/lib/protobuf/symbol_database.py new file mode 100644 index 0000000..fdcf8cf --- /dev/null +++ b/lib/protobuf/symbol_database.py @@ -0,0 +1,194 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""A database of Python protocol buffer generated symbols. + +SymbolDatabase is the MessageFactory for messages generated at compile time, +and makes it easy to create new instances of a registered type, given only the +type's protocol buffer symbol name. + +Example usage:: + + db = symbol_database.SymbolDatabase() + + # Register symbols of interest, from one or multiple files. + db.RegisterFileDescriptor(my_proto_pb2.DESCRIPTOR) + db.RegisterMessage(my_proto_pb2.MyMessage) + db.RegisterEnumDescriptor(my_proto_pb2.MyEnum.DESCRIPTOR) + + # The database can be used as a MessageFactory, to generate types based on + # their name: + types = db.GetMessages(['my_proto.proto']) + my_message_instance = types['MyMessage']() + + # The database's underlying descriptor pool can be queried, so it's not + # necessary to know a type's filename to be able to generate it: + filename = db.pool.FindFileContainingSymbol('MyMessage') + my_message_instance = db.GetMessages([filename])['MyMessage']() + + # This functionality is also provided directly via a convenience method: + my_message_instance = db.GetSymbol('MyMessage')() +""" + + +from google.protobuf.internal import api_implementation +from google.protobuf import descriptor_pool +from google.protobuf import message_factory + + +class SymbolDatabase(message_factory.MessageFactory): + """A database of Python generated symbols.""" + + def RegisterMessage(self, message): + """Registers the given message type in the local database. + + Calls to GetSymbol() and GetMessages() will return messages registered here. + + Args: + message: A :class:`google.protobuf.message.Message` subclass (or + instance); its descriptor will be registered. + + Returns: + The provided message. + """ + + desc = message.DESCRIPTOR + self._classes[desc] = message + self.RegisterMessageDescriptor(desc) + return message + + def RegisterMessageDescriptor(self, message_descriptor): + """Registers the given message descriptor in the local database. + + Args: + message_descriptor (Descriptor): the message descriptor to add. + """ + if api_implementation.Type() == 'python': + # pylint: disable=protected-access + self.pool._AddDescriptor(message_descriptor) + + def RegisterEnumDescriptor(self, enum_descriptor): + """Registers the given enum descriptor in the local database. + + Args: + enum_descriptor (EnumDescriptor): The enum descriptor to register. + + Returns: + EnumDescriptor: The provided descriptor. + """ + if api_implementation.Type() == 'python': + # pylint: disable=protected-access + self.pool._AddEnumDescriptor(enum_descriptor) + return enum_descriptor + + def RegisterServiceDescriptor(self, service_descriptor): + """Registers the given service descriptor in the local database. + + Args: + service_descriptor (ServiceDescriptor): the service descriptor to + register. + """ + if api_implementation.Type() == 'python': + # pylint: disable=protected-access + self.pool._AddServiceDescriptor(service_descriptor) + + def RegisterFileDescriptor(self, file_descriptor): + """Registers the given file descriptor in the local database. + + Args: + file_descriptor (FileDescriptor): The file descriptor to register. + """ + if api_implementation.Type() == 'python': + # pylint: disable=protected-access + self.pool._InternalAddFileDescriptor(file_descriptor) + + def GetSymbol(self, symbol): + """Tries to find a symbol in the local database. + + Currently, this method only returns message.Message instances, however, if + may be extended in future to support other symbol types. + + Args: + symbol (str): a protocol buffer symbol. + + Returns: + A Python class corresponding to the symbol. + + Raises: + KeyError: if the symbol could not be found. + """ + + return self._classes[self.pool.FindMessageTypeByName(symbol)] + + def GetMessages(self, files): + # TODO(amauryfa): Fix the differences with MessageFactory. + """Gets all registered messages from a specified file. + + Only messages already created and registered will be returned; (this is the + case for imported _pb2 modules) + But unlike MessageFactory, this version also returns already defined nested + messages, but does not register any message extensions. + + Args: + files (list[str]): The file names to extract messages from. + + Returns: + A dictionary mapping proto names to the message classes. + + Raises: + KeyError: if a file could not be found. + """ + + def _GetAllMessages(desc): + """Walk a message Descriptor and recursively yields all message names.""" + yield desc + for msg_desc in desc.nested_types: + for nested_desc in _GetAllMessages(msg_desc): + yield nested_desc + + result = {} + for file_name in files: + file_desc = self.pool.FindFileByName(file_name) + for msg_desc in file_desc.message_types_by_name.values(): + for desc in _GetAllMessages(msg_desc): + try: + result[desc.full_name] = self._classes[desc] + except KeyError: + # This descriptor has no registered class, skip it. + pass + return result + + +_DEFAULT = SymbolDatabase(pool=descriptor_pool.Default()) + + +def Default(): + """Returns the default SymbolDatabase.""" + return _DEFAULT diff --git a/lib/protobuf/text_encoding.py b/lib/protobuf/text_encoding.py new file mode 100644 index 0000000..759cf11 --- /dev/null +++ b/lib/protobuf/text_encoding.py @@ -0,0 +1,110 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Encoding related utilities.""" +import re + +_cescape_chr_to_symbol_map = {} +_cescape_chr_to_symbol_map[9] = r'\t' # optional escape +_cescape_chr_to_symbol_map[10] = r'\n' # optional escape +_cescape_chr_to_symbol_map[13] = r'\r' # optional escape +_cescape_chr_to_symbol_map[34] = r'\"' # necessary escape +_cescape_chr_to_symbol_map[39] = r"\'" # optional escape +_cescape_chr_to_symbol_map[92] = r'\\' # necessary escape + +# Lookup table for unicode +_cescape_unicode_to_str = [chr(i) for i in range(0, 256)] +for byte, string in _cescape_chr_to_symbol_map.items(): + _cescape_unicode_to_str[byte] = string + +# Lookup table for non-utf8, with necessary escapes at (o >= 127 or o < 32) +_cescape_byte_to_str = ([r'\%03o' % i for i in range(0, 32)] + + [chr(i) for i in range(32, 127)] + + [r'\%03o' % i for i in range(127, 256)]) +for byte, string in _cescape_chr_to_symbol_map.items(): + _cescape_byte_to_str[byte] = string +del byte, string + + +def CEscape(text, as_utf8): + # type: (...) -> str + """Escape a bytes string for use in an text protocol buffer. + + Args: + text: A byte string to be escaped. + as_utf8: Specifies if result may contain non-ASCII characters. + In Python 3 this allows unescaped non-ASCII Unicode characters. + In Python 2 the return value will be valid UTF-8 rather than only ASCII. + Returns: + Escaped string (str). + """ + # Python's text.encode() 'string_escape' or 'unicode_escape' codecs do not + # satisfy our needs; they encodes unprintable characters using two-digit hex + # escapes whereas our C++ unescaping function allows hex escapes to be any + # length. So, "\0011".encode('string_escape') ends up being "\\x011", which + # will be decoded in C++ as a single-character string with char code 0x11. + text_is_unicode = isinstance(text, str) + if as_utf8 and text_is_unicode: + # We're already unicode, no processing beyond control char escapes. + return text.translate(_cescape_chr_to_symbol_map) + ord_ = ord if text_is_unicode else lambda x: x # bytes iterate as ints. + if as_utf8: + return ''.join(_cescape_unicode_to_str[ord_(c)] for c in text) + return ''.join(_cescape_byte_to_str[ord_(c)] for c in text) + + +_CUNESCAPE_HEX = re.compile(r'(\\+)x([0-9a-fA-F])(?![0-9a-fA-F])') + + +def CUnescape(text): + # type: (str) -> bytes + """Unescape a text string with C-style escape sequences to UTF-8 bytes. + + Args: + text: The data to parse in a str. + Returns: + A byte string. + """ + + def ReplaceHex(m): + # Only replace the match if the number of leading back slashes is odd. i.e. + # the slash itself is not escaped. + if len(m.group(1)) & 1: + return m.group(1) + 'x0' + m.group(2) + return m.group(0) + + # This is required because the 'string_escape' encoding doesn't + # allow single-digit hex escapes (like '\xf'). + result = _CUNESCAPE_HEX.sub(ReplaceHex, text) + + return (result.encode('utf-8') # Make it bytes to allow decode. + .decode('unicode_escape') + # Make it bytes again to return the proper type. + .encode('raw_unicode_escape')) diff --git a/lib/protobuf/text_format.py b/lib/protobuf/text_format.py new file mode 100644 index 0000000..412385c --- /dev/null +++ b/lib/protobuf/text_format.py @@ -0,0 +1,1795 @@ +# Protocol Buffers - Google's data interchange format +# Copyright 2008 Google Inc. All rights reserved. +# https://developers.google.com/protocol-buffers/ +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following disclaimer +# in the documentation and/or other materials provided with the +# distribution. +# * Neither the name of Google Inc. nor the names of its +# contributors may be used to endorse or promote products derived from +# this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +"""Contains routines for printing protocol messages in text format. + +Simple usage example:: + + # Create a proto object and serialize it to a text proto string. + message = my_proto_pb2.MyMessage(foo='bar') + text_proto = text_format.MessageToString(message) + + # Parse a text proto string. + message = text_format.Parse(text_proto, my_proto_pb2.MyMessage()) +""" + +__author__ = 'kenton@google.com (Kenton Varda)' + +# TODO(b/129989314) Import thread contention leads to test failures. +import encodings.raw_unicode_escape # pylint: disable=unused-import +import encodings.unicode_escape # pylint: disable=unused-import +import io +import math +import re + +from google.protobuf.internal import decoder +from google.protobuf.internal import type_checkers +from google.protobuf import descriptor +from google.protobuf import text_encoding + +# pylint: disable=g-import-not-at-top +__all__ = ['MessageToString', 'Parse', 'PrintMessage', 'PrintField', + 'PrintFieldValue', 'Merge', 'MessageToBytes'] + +_INTEGER_CHECKERS = (type_checkers.Uint32ValueChecker(), + type_checkers.Int32ValueChecker(), + type_checkers.Uint64ValueChecker(), + type_checkers.Int64ValueChecker()) +_FLOAT_INFINITY = re.compile('-?inf(?:inity)?f?$', re.IGNORECASE) +_FLOAT_NAN = re.compile('nanf?$', re.IGNORECASE) +_QUOTES = frozenset(("'", '"')) +_ANY_FULL_TYPE_NAME = 'google.protobuf.Any' + + +class Error(Exception): + """Top-level module error for text_format.""" + + +class ParseError(Error): + """Thrown in case of text parsing or tokenizing error.""" + + def __init__(self, message=None, line=None, column=None): + if message is not None and line is not None: + loc = str(line) + if column is not None: + loc += ':{0}'.format(column) + message = '{0} : {1}'.format(loc, message) + if message is not None: + super(ParseError, self).__init__(message) + else: + super(ParseError, self).__init__() + self._line = line + self._column = column + + def GetLine(self): + return self._line + + def GetColumn(self): + return self._column + + +class TextWriter(object): + + def __init__(self, as_utf8): + self._writer = io.StringIO() + + def write(self, val): + return self._writer.write(val) + + def close(self): + return self._writer.close() + + def getvalue(self): + return self._writer.getvalue() + + +def MessageToString( + message, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + use_field_number=False, + descriptor_pool=None, + indent=0, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): + # type: (...) -> str + """Convert protobuf message to text format. + + Double values can be formatted compactly with 15 digits of + precision (which is the most that IEEE 754 "double" can guarantee) + using double_format='.15g'. To ensure that converting to text and back to a + proto will result in an identical value, double_format='.17g' should be used. + + Args: + message: The protocol buffers message. + as_utf8: Return unescaped Unicode for non-ASCII characters. + In Python 3 actual Unicode characters may appear as is in strings. + In Python 2 the return value will be valid UTF-8 rather than only ASCII. + as_one_line: Don't introduce newlines between fields. + use_short_repeated_primitives: Use short repeated format for primitives. + pointy_brackets: If True, use angle brackets instead of curly braces for + nesting. + use_index_order: If True, fields of a proto message will be printed using + the order defined in source code instead of the field number, extensions + will be printed at the end of the message and their relative order is + determined by the extension number. By default, use the field number + order. + float_format (str): If set, use this to specify float field formatting + (per the "Format Specification Mini-Language"); otherwise, shortest float + that has same value in wire will be printed. Also affect double field + if double_format is not set but float_format is set. + double_format (str): If set, use this to specify double field formatting + (per the "Format Specification Mini-Language"); if it is not set but + float_format is set, use float_format. Otherwise, use ``str()`` + use_field_number: If True, print field numbers instead of names. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + indent (int): The initial indent level, in terms of spaces, for pretty + print. + message_formatter (function(message, indent, as_one_line) -> unicode|None): + Custom formatter for selected sub-messages (usually based on message + type). Use to pretty print parts of the protobuf for easier diffing. + print_unknown_fields: If True, unknown fields will be printed. + force_colon: If set, a colon will be added after the field name even if the + field is a proto message. + + Returns: + str: A string of the text formatted protocol buffer message. + """ + out = TextWriter(as_utf8) + printer = _Printer( + out, + indent, + as_utf8, + as_one_line, + use_short_repeated_primitives, + pointy_brackets, + use_index_order, + float_format, + double_format, + use_field_number, + descriptor_pool, + message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon) + printer.PrintMessage(message) + result = out.getvalue() + out.close() + if as_one_line: + return result.rstrip() + return result + + +def MessageToBytes(message, **kwargs): + # type: (...) -> bytes + """Convert protobuf message to encoded text format. See MessageToString.""" + text = MessageToString(message, **kwargs) + if isinstance(text, bytes): + return text + codec = 'utf-8' if kwargs.get('as_utf8') else 'ascii' + return text.encode(codec) + + +def _IsMapEntry(field): + return (field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.message_type.has_options and + field.message_type.GetOptions().map_entry) + + +def PrintMessage(message, + out, + indent=0, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + use_field_number=False, + descriptor_pool=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): + printer = _Printer( + out=out, indent=indent, as_utf8=as_utf8, + as_one_line=as_one_line, + use_short_repeated_primitives=use_short_repeated_primitives, + pointy_brackets=pointy_brackets, + use_index_order=use_index_order, + float_format=float_format, + double_format=double_format, + use_field_number=use_field_number, + descriptor_pool=descriptor_pool, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon) + printer.PrintMessage(message) + + +def PrintField(field, + value, + out, + indent=0, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): + """Print a single field name/value pair.""" + printer = _Printer(out, indent, as_utf8, as_one_line, + use_short_repeated_primitives, pointy_brackets, + use_index_order, float_format, double_format, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon) + printer.PrintField(field, value) + + +def PrintFieldValue(field, + value, + out, + indent=0, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): + """Print a single field value (not including name).""" + printer = _Printer(out, indent, as_utf8, as_one_line, + use_short_repeated_primitives, pointy_brackets, + use_index_order, float_format, double_format, + message_formatter=message_formatter, + print_unknown_fields=print_unknown_fields, + force_colon=force_colon) + printer.PrintFieldValue(field, value) + + +def _BuildMessageFromTypeName(type_name, descriptor_pool): + """Returns a protobuf message instance. + + Args: + type_name: Fully-qualified protobuf message type name string. + descriptor_pool: DescriptorPool instance. + + Returns: + A Message instance of type matching type_name, or None if the a Descriptor + wasn't found matching type_name. + """ + # pylint: disable=g-import-not-at-top + if descriptor_pool is None: + from google.protobuf import descriptor_pool as pool_mod + descriptor_pool = pool_mod.Default() + from google.protobuf import symbol_database + database = symbol_database.Default() + try: + message_descriptor = descriptor_pool.FindMessageTypeByName(type_name) + except KeyError: + return None + message_type = database.GetPrototype(message_descriptor) + return message_type() + + +# These values must match WireType enum in google/protobuf/wire_format.h. +WIRETYPE_LENGTH_DELIMITED = 2 +WIRETYPE_START_GROUP = 3 + + +class _Printer(object): + """Text format printer for protocol message.""" + + def __init__( + self, + out, + indent=0, + as_utf8=False, + as_one_line=False, + use_short_repeated_primitives=False, + pointy_brackets=False, + use_index_order=False, + float_format=None, + double_format=None, + use_field_number=False, + descriptor_pool=None, + message_formatter=None, + print_unknown_fields=False, + force_colon=False): + """Initialize the Printer. + + Double values can be formatted compactly with 15 digits of precision + (which is the most that IEEE 754 "double" can guarantee) using + double_format='.15g'. To ensure that converting to text and back to a proto + will result in an identical value, double_format='.17g' should be used. + + Args: + out: To record the text format result. + indent: The initial indent level for pretty print. + as_utf8: Return unescaped Unicode for non-ASCII characters. + In Python 3 actual Unicode characters may appear as is in strings. + In Python 2 the return value will be valid UTF-8 rather than ASCII. + as_one_line: Don't introduce newlines between fields. + use_short_repeated_primitives: Use short repeated format for primitives. + pointy_brackets: If True, use angle brackets instead of curly braces for + nesting. + use_index_order: If True, print fields of a proto message using the order + defined in source code instead of the field number. By default, use the + field number order. + float_format: If set, use this to specify float field formatting + (per the "Format Specification Mini-Language"); otherwise, shortest + float that has same value in wire will be printed. Also affect double + field if double_format is not set but float_format is set. + double_format: If set, use this to specify double field formatting + (per the "Format Specification Mini-Language"); if it is not set but + float_format is set, use float_format. Otherwise, str() is used. + use_field_number: If True, print field numbers instead of names. + descriptor_pool: A DescriptorPool used to resolve Any types. + message_formatter: A function(message, indent, as_one_line): unicode|None + to custom format selected sub-messages (usually based on message type). + Use to pretty print parts of the protobuf for easier diffing. + print_unknown_fields: If True, unknown fields will be printed. + force_colon: If set, a colon will be added after the field name even if + the field is a proto message. + """ + self.out = out + self.indent = indent + self.as_utf8 = as_utf8 + self.as_one_line = as_one_line + self.use_short_repeated_primitives = use_short_repeated_primitives + self.pointy_brackets = pointy_brackets + self.use_index_order = use_index_order + self.float_format = float_format + if double_format is not None: + self.double_format = double_format + else: + self.double_format = float_format + self.use_field_number = use_field_number + self.descriptor_pool = descriptor_pool + self.message_formatter = message_formatter + self.print_unknown_fields = print_unknown_fields + self.force_colon = force_colon + + def _TryPrintAsAnyMessage(self, message): + """Serializes if message is a google.protobuf.Any field.""" + if '/' not in message.type_url: + return False + packed_message = _BuildMessageFromTypeName(message.TypeName(), + self.descriptor_pool) + if packed_message: + packed_message.MergeFromString(message.value) + colon = ':' if self.force_colon else '' + self.out.write('%s[%s]%s ' % (self.indent * ' ', message.type_url, colon)) + self._PrintMessageFieldValue(packed_message) + self.out.write(' ' if self.as_one_line else '\n') + return True + else: + return False + + def _TryCustomFormatMessage(self, message): + formatted = self.message_formatter(message, self.indent, self.as_one_line) + if formatted is None: + return False + + out = self.out + out.write(' ' * self.indent) + out.write(formatted) + out.write(' ' if self.as_one_line else '\n') + return True + + def PrintMessage(self, message): + """Convert protobuf message to text format. + + Args: + message: The protocol buffers message. + """ + if self.message_formatter and self._TryCustomFormatMessage(message): + return + if (message.DESCRIPTOR.full_name == _ANY_FULL_TYPE_NAME and + self._TryPrintAsAnyMessage(message)): + return + fields = message.ListFields() + if self.use_index_order: + fields.sort( + key=lambda x: x[0].number if x[0].is_extension else x[0].index) + for field, value in fields: + if _IsMapEntry(field): + for key in sorted(value): + # This is slow for maps with submessage entries because it copies the + # entire tree. Unfortunately this would take significant refactoring + # of this file to work around. + # + # TODO(haberman): refactor and optimize if this becomes an issue. + entry_submsg = value.GetEntryClass()(key=key, value=value[key]) + self.PrintField(field, entry_submsg) + elif field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + if (self.use_short_repeated_primitives + and field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE + and field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_STRING): + self._PrintShortRepeatedPrimitivesValue(field, value) + else: + for element in value: + self.PrintField(field, element) + else: + self.PrintField(field, value) + + if self.print_unknown_fields: + self._PrintUnknownFields(message.UnknownFields()) + + def _PrintUnknownFields(self, unknown_fields): + """Print unknown fields.""" + out = self.out + for field in unknown_fields: + out.write(' ' * self.indent) + out.write(str(field.field_number)) + if field.wire_type == WIRETYPE_START_GROUP: + if self.as_one_line: + out.write(' { ') + else: + out.write(' {\n') + self.indent += 2 + + self._PrintUnknownFields(field.data) + + if self.as_one_line: + out.write('} ') + else: + self.indent -= 2 + out.write(' ' * self.indent + '}\n') + elif field.wire_type == WIRETYPE_LENGTH_DELIMITED: + try: + # If this field is parseable as a Message, it is probably + # an embedded message. + # pylint: disable=protected-access + (embedded_unknown_message, pos) = decoder._DecodeUnknownFieldSet( + memoryview(field.data), 0, len(field.data)) + except Exception: # pylint: disable=broad-except + pos = 0 + + if pos == len(field.data): + if self.as_one_line: + out.write(' { ') + else: + out.write(' {\n') + self.indent += 2 + + self._PrintUnknownFields(embedded_unknown_message) + + if self.as_one_line: + out.write('} ') + else: + self.indent -= 2 + out.write(' ' * self.indent + '}\n') + else: + # A string or bytes field. self.as_utf8 may not work. + out.write(': \"') + out.write(text_encoding.CEscape(field.data, False)) + out.write('\" ' if self.as_one_line else '\"\n') + else: + # varint, fixed32, fixed64 + out.write(': ') + out.write(str(field.data)) + out.write(' ' if self.as_one_line else '\n') + + def _PrintFieldName(self, field): + """Print field name.""" + out = self.out + out.write(' ' * self.indent) + if self.use_field_number: + out.write(str(field.number)) + else: + if field.is_extension: + out.write('[') + if (field.containing_type.GetOptions().message_set_wire_format and + field.type == descriptor.FieldDescriptor.TYPE_MESSAGE and + field.label == descriptor.FieldDescriptor.LABEL_OPTIONAL): + out.write(field.message_type.full_name) + else: + out.write(field.full_name) + out.write(']') + elif field.type == descriptor.FieldDescriptor.TYPE_GROUP: + # For groups, use the capitalized name. + out.write(field.message_type.name) + else: + out.write(field.name) + + if (self.force_colon or + field.cpp_type != descriptor.FieldDescriptor.CPPTYPE_MESSAGE): + # The colon is optional in this case, but our cross-language golden files + # don't include it. Here, the colon is only included if force_colon is + # set to True + out.write(':') + + def PrintField(self, field, value): + """Print a single field name/value pair.""" + self._PrintFieldName(field) + self.out.write(' ') + self.PrintFieldValue(field, value) + self.out.write(' ' if self.as_one_line else '\n') + + def _PrintShortRepeatedPrimitivesValue(self, field, value): + """"Prints short repeated primitives value.""" + # Note: this is called only when value has at least one element. + self._PrintFieldName(field) + self.out.write(' [') + for i in range(len(value) - 1): + self.PrintFieldValue(field, value[i]) + self.out.write(', ') + self.PrintFieldValue(field, value[-1]) + self.out.write(']') + self.out.write(' ' if self.as_one_line else '\n') + + def _PrintMessageFieldValue(self, value): + if self.pointy_brackets: + openb = '<' + closeb = '>' + else: + openb = '{' + closeb = '}' + + if self.as_one_line: + self.out.write('%s ' % openb) + self.PrintMessage(value) + self.out.write(closeb) + else: + self.out.write('%s\n' % openb) + self.indent += 2 + self.PrintMessage(value) + self.indent -= 2 + self.out.write(' ' * self.indent + closeb) + + def PrintFieldValue(self, field, value): + """Print a single field value (not including name). + + For repeated fields, the value should be a single element. + + Args: + field: The descriptor of the field to be printed. + value: The value of the field. + """ + out = self.out + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + self._PrintMessageFieldValue(value) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_ENUM: + enum_value = field.enum_type.values_by_number.get(value, None) + if enum_value is not None: + out.write(enum_value.name) + else: + out.write(str(value)) + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_STRING: + out.write('\"') + if isinstance(value, str) and not self.as_utf8: + out_value = value.encode('utf-8') + else: + out_value = value + if field.type == descriptor.FieldDescriptor.TYPE_BYTES: + # We always need to escape all binary data in TYPE_BYTES fields. + out_as_utf8 = False + else: + out_as_utf8 = self.as_utf8 + out.write(text_encoding.CEscape(out_value, out_as_utf8)) + out.write('\"') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_BOOL: + if value: + out.write('true') + else: + out.write('false') + elif field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_FLOAT: + if self.float_format is not None: + out.write('{1:{0}}'.format(self.float_format, value)) + else: + if math.isnan(value): + out.write(str(value)) + else: + out.write(str(type_checkers.ToShortestFloat(value))) + elif (field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_DOUBLE and + self.double_format is not None): + out.write('{1:{0}}'.format(self.double_format, value)) + else: + out.write(str(value)) + + +def Parse(text, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False): + """Parses a text representation of a protocol message into a message. + + NOTE: for historical reasons this function does not clear the input + message. This is different from what the binary msg.ParseFrom(...) does. + If text contains a field already set in message, the value is appended if the + field is repeated. Otherwise, an error is raised. + + Example:: + + a = MyProto() + a.repeated_field.append('test') + b = MyProto() + + # Repeated fields are combined + text_format.Parse(repr(a), b) + text_format.Parse(repr(a), b) # repeated_field contains ["test", "test"] + + # Non-repeated fields cannot be overwritten + a.singular_field = 1 + b.singular_field = 2 + text_format.Parse(repr(a), b) # ParseError + + # Binary version: + b.ParseFromString(a.SerializeToString()) # repeated_field is now "test" + + Caller is responsible for clearing the message as needed. + + Args: + text (str): Message text representation. + message (Message): A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep + parsing. Avoid to use this option if possible. It may hide some + errors (e.g. spelling error on field name) + + Returns: + Message: The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + return ParseLines(text.split(b'\n' if isinstance(text, bytes) else u'\n'), + message, + allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field) + + +def Merge(text, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False): + """Parses a text representation of a protocol message into a message. + + Like Parse(), but allows repeated values for a non-repeated field, and uses + the last one. This means any non-repeated, top-level fields specified in text + replace those in the message. + + Args: + text (str): Message text representation. + message (Message): A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool (DescriptorPool): Descriptor pool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep + parsing. Avoid to use this option if possible. It may hide some + errors (e.g. spelling error on field name) + + Returns: + Message: The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + return MergeLines( + text.split(b'\n' if isinstance(text, bytes) else u'\n'), + message, + allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field) + + +def ParseLines(lines, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False): + """Parses a text representation of a protocol message into a message. + + See Parse() for caveats. + + Args: + lines: An iterable of lines of a message's text representation. + message: A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool: A DescriptorPool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep + parsing. Avoid to use this option if possible. It may hide some + errors (e.g. spelling error on field name) + + Returns: + The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + parser = _Parser(allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field) + return parser.ParseLines(lines, message) + + +def MergeLines(lines, + message, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False): + """Parses a text representation of a protocol message into a message. + + See Merge() for more details. + + Args: + lines: An iterable of lines of a message's text representation. + message: A protocol buffer message to merge into. + allow_unknown_extension: if True, skip over missing extensions and keep + parsing + allow_field_number: if True, both field number and field name are allowed. + descriptor_pool: A DescriptorPool used to resolve Any types. + allow_unknown_field: if True, skip over unknown field and keep + parsing. Avoid to use this option if possible. It may hide some + errors (e.g. spelling error on field name) + + Returns: + The same message passed as argument. + + Raises: + ParseError: On text parsing problems. + """ + parser = _Parser(allow_unknown_extension, + allow_field_number, + descriptor_pool=descriptor_pool, + allow_unknown_field=allow_unknown_field) + return parser.MergeLines(lines, message) + + +class _Parser(object): + """Text format parser for protocol message.""" + + def __init__(self, + allow_unknown_extension=False, + allow_field_number=False, + descriptor_pool=None, + allow_unknown_field=False): + self.allow_unknown_extension = allow_unknown_extension + self.allow_field_number = allow_field_number + self.descriptor_pool = descriptor_pool + self.allow_unknown_field = allow_unknown_field + + def ParseLines(self, lines, message): + """Parses a text representation of a protocol message into a message.""" + self._allow_multiple_scalars = False + self._ParseOrMerge(lines, message) + return message + + def MergeLines(self, lines, message): + """Merges a text representation of a protocol message into a message.""" + self._allow_multiple_scalars = True + self._ParseOrMerge(lines, message) + return message + + def _ParseOrMerge(self, lines, message): + """Converts a text representation of a protocol message into a message. + + Args: + lines: Lines of a message's text representation. + message: A protocol buffer message to merge into. + + Raises: + ParseError: On text parsing problems. + """ + # Tokenize expects native str lines. + str_lines = ( + line if isinstance(line, str) else line.decode('utf-8') + for line in lines) + tokenizer = Tokenizer(str_lines) + while not tokenizer.AtEnd(): + self._MergeField(tokenizer, message) + + def _MergeField(self, tokenizer, message): + """Merges a single protocol message field into a message. + + Args: + tokenizer: A tokenizer to parse the field name and values. + message: A protocol message to record the data. + + Raises: + ParseError: In case of text parsing problems. + """ + message_descriptor = message.DESCRIPTOR + if (message_descriptor.full_name == _ANY_FULL_TYPE_NAME and + tokenizer.TryConsume('[')): + type_url_prefix, packed_type_name = self._ConsumeAnyTypeUrl(tokenizer) + tokenizer.Consume(']') + tokenizer.TryConsume(':') + if tokenizer.TryConsume('<'): + expanded_any_end_token = '>' + else: + tokenizer.Consume('{') + expanded_any_end_token = '}' + expanded_any_sub_message = _BuildMessageFromTypeName(packed_type_name, + self.descriptor_pool) + if not expanded_any_sub_message: + raise ParseError('Type %s not found in descriptor pool' % + packed_type_name) + while not tokenizer.TryConsume(expanded_any_end_token): + if tokenizer.AtEnd(): + raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % + (expanded_any_end_token,)) + self._MergeField(tokenizer, expanded_any_sub_message) + deterministic = False + + message.Pack(expanded_any_sub_message, + type_url_prefix=type_url_prefix, + deterministic=deterministic) + return + + if tokenizer.TryConsume('['): + name = [tokenizer.ConsumeIdentifier()] + while tokenizer.TryConsume('.'): + name.append(tokenizer.ConsumeIdentifier()) + name = '.'.join(name) + + if not message_descriptor.is_extendable: + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" does not have extensions.' % + message_descriptor.full_name) + # pylint: disable=protected-access + field = message.Extensions._FindExtensionByName(name) + # pylint: enable=protected-access + + + if not field: + if self.allow_unknown_extension: + field = None + else: + raise tokenizer.ParseErrorPreviousToken( + 'Extension "%s" not registered. ' + 'Did you import the _pb2 module which defines it? ' + 'If you are trying to place the extension in the MessageSet ' + 'field of another message that is in an Any or MessageSet field, ' + 'that message\'s _pb2 module must be imported as well' % name) + elif message_descriptor != field.containing_type: + raise tokenizer.ParseErrorPreviousToken( + 'Extension "%s" does not extend message type "%s".' % + (name, message_descriptor.full_name)) + + tokenizer.Consume(']') + + else: + name = tokenizer.ConsumeIdentifierOrNumber() + if self.allow_field_number and name.isdigit(): + number = ParseInteger(name, True, True) + field = message_descriptor.fields_by_number.get(number, None) + if not field and message_descriptor.is_extendable: + field = message.Extensions._FindExtensionByNumber(number) + else: + field = message_descriptor.fields_by_name.get(name, None) + + # Group names are expected to be capitalized as they appear in the + # .proto file, which actually matches their type names, not their field + # names. + if not field: + field = message_descriptor.fields_by_name.get(name.lower(), None) + if field and field.type != descriptor.FieldDescriptor.TYPE_GROUP: + field = None + + if (field and field.type == descriptor.FieldDescriptor.TYPE_GROUP and + field.message_type.name != name): + field = None + + if not field and not self.allow_unknown_field: + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" has no field named "%s".' % + (message_descriptor.full_name, name)) + + if field: + if not self._allow_multiple_scalars and field.containing_oneof: + # Check if there's a different field set in this oneof. + # Note that we ignore the case if the same field was set before, and we + # apply _allow_multiple_scalars to non-scalar fields as well. + which_oneof = message.WhichOneof(field.containing_oneof.name) + if which_oneof is not None and which_oneof != field.name: + raise tokenizer.ParseErrorPreviousToken( + 'Field "%s" is specified along with field "%s", another member ' + 'of oneof "%s" for message type "%s".' % + (field.name, which_oneof, field.containing_oneof.name, + message_descriptor.full_name)) + + if field.cpp_type == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + tokenizer.TryConsume(':') + merger = self._MergeMessageField + else: + tokenizer.Consume(':') + merger = self._MergeScalarField + + if (field.label == descriptor.FieldDescriptor.LABEL_REPEATED and + tokenizer.TryConsume('[')): + # Short repeated format, e.g. "foo: [1, 2, 3]" + if not tokenizer.TryConsume(']'): + while True: + merger(tokenizer, message, field) + if tokenizer.TryConsume(']'): + break + tokenizer.Consume(',') + + else: + merger(tokenizer, message, field) + + else: # Proto field is unknown. + assert (self.allow_unknown_extension or self.allow_unknown_field) + _SkipFieldContents(tokenizer) + + # For historical reasons, fields may optionally be separated by commas or + # semicolons. + if not tokenizer.TryConsume(','): + tokenizer.TryConsume(';') + + + def _ConsumeAnyTypeUrl(self, tokenizer): + """Consumes a google.protobuf.Any type URL and returns the type name.""" + # Consume "type.googleapis.com/". + prefix = [tokenizer.ConsumeIdentifier()] + tokenizer.Consume('.') + prefix.append(tokenizer.ConsumeIdentifier()) + tokenizer.Consume('.') + prefix.append(tokenizer.ConsumeIdentifier()) + tokenizer.Consume('/') + # Consume the fully-qualified type name. + name = [tokenizer.ConsumeIdentifier()] + while tokenizer.TryConsume('.'): + name.append(tokenizer.ConsumeIdentifier()) + return '.'.join(prefix), '.'.join(name) + + def _MergeMessageField(self, tokenizer, message, field): + """Merges a single scalar field into a message. + + Args: + tokenizer: A tokenizer to parse the field value. + message: The message of which field is a member. + field: The descriptor of the field to be merged. + + Raises: + ParseError: In case of text parsing problems. + """ + is_map_entry = _IsMapEntry(field) + + if tokenizer.TryConsume('<'): + end_token = '>' + else: + tokenizer.Consume('{') + end_token = '}' + + if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + if field.is_extension: + sub_message = message.Extensions[field].add() + elif is_map_entry: + sub_message = getattr(message, field.name).GetEntryClass()() + else: + sub_message = getattr(message, field.name).add() + else: + if field.is_extension: + if (not self._allow_multiple_scalars and + message.HasExtension(field)): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" extensions.' % + (message.DESCRIPTOR.full_name, field.full_name)) + sub_message = message.Extensions[field] + else: + # Also apply _allow_multiple_scalars to message field. + # TODO(jieluo): Change to _allow_singular_overwrites. + if (not self._allow_multiple_scalars and + message.HasField(field.name)): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" fields.' % + (message.DESCRIPTOR.full_name, field.name)) + sub_message = getattr(message, field.name) + sub_message.SetInParent() + + while not tokenizer.TryConsume(end_token): + if tokenizer.AtEnd(): + raise tokenizer.ParseErrorPreviousToken('Expected "%s".' % (end_token,)) + self._MergeField(tokenizer, sub_message) + + if is_map_entry: + value_cpptype = field.message_type.fields_by_name['value'].cpp_type + if value_cpptype == descriptor.FieldDescriptor.CPPTYPE_MESSAGE: + value = getattr(message, field.name)[sub_message.key] + value.CopyFrom(sub_message.value) + else: + getattr(message, field.name)[sub_message.key] = sub_message.value + + @staticmethod + def _IsProto3Syntax(message): + message_descriptor = message.DESCRIPTOR + return (hasattr(message_descriptor, 'syntax') and + message_descriptor.syntax == 'proto3') + + def _MergeScalarField(self, tokenizer, message, field): + """Merges a single scalar field into a message. + + Args: + tokenizer: A tokenizer to parse the field value. + message: A protocol message to record the data. + field: The descriptor of the field to be merged. + + Raises: + ParseError: In case of text parsing problems. + RuntimeError: On runtime errors. + """ + _ = self.allow_unknown_extension + value = None + + if field.type in (descriptor.FieldDescriptor.TYPE_INT32, + descriptor.FieldDescriptor.TYPE_SINT32, + descriptor.FieldDescriptor.TYPE_SFIXED32): + value = _ConsumeInt32(tokenizer) + elif field.type in (descriptor.FieldDescriptor.TYPE_INT64, + descriptor.FieldDescriptor.TYPE_SINT64, + descriptor.FieldDescriptor.TYPE_SFIXED64): + value = _ConsumeInt64(tokenizer) + elif field.type in (descriptor.FieldDescriptor.TYPE_UINT32, + descriptor.FieldDescriptor.TYPE_FIXED32): + value = _ConsumeUint32(tokenizer) + elif field.type in (descriptor.FieldDescriptor.TYPE_UINT64, + descriptor.FieldDescriptor.TYPE_FIXED64): + value = _ConsumeUint64(tokenizer) + elif field.type in (descriptor.FieldDescriptor.TYPE_FLOAT, + descriptor.FieldDescriptor.TYPE_DOUBLE): + value = tokenizer.ConsumeFloat() + elif field.type == descriptor.FieldDescriptor.TYPE_BOOL: + value = tokenizer.ConsumeBool() + elif field.type == descriptor.FieldDescriptor.TYPE_STRING: + value = tokenizer.ConsumeString() + elif field.type == descriptor.FieldDescriptor.TYPE_BYTES: + value = tokenizer.ConsumeByteString() + elif field.type == descriptor.FieldDescriptor.TYPE_ENUM: + value = tokenizer.ConsumeEnum(field) + else: + raise RuntimeError('Unknown field type %d' % field.type) + + if field.label == descriptor.FieldDescriptor.LABEL_REPEATED: + if field.is_extension: + message.Extensions[field].append(value) + else: + getattr(message, field.name).append(value) + else: + if field.is_extension: + if (not self._allow_multiple_scalars and + not self._IsProto3Syntax(message) and + message.HasExtension(field)): + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" extensions.' % + (message.DESCRIPTOR.full_name, field.full_name)) + else: + message.Extensions[field] = value + else: + duplicate_error = False + if not self._allow_multiple_scalars: + if self._IsProto3Syntax(message): + # Proto3 doesn't represent presence so we try best effort to check + # multiple scalars by compare to default values. + duplicate_error = bool(getattr(message, field.name)) + else: + duplicate_error = message.HasField(field.name) + + if duplicate_error: + raise tokenizer.ParseErrorPreviousToken( + 'Message type "%s" should not have multiple "%s" fields.' % + (message.DESCRIPTOR.full_name, field.name)) + else: + setattr(message, field.name, value) + + +def _SkipFieldContents(tokenizer): + """Skips over contents (value or message) of a field. + + Args: + tokenizer: A tokenizer to parse the field name and values. + """ + # Try to guess the type of this field. + # If this field is not a message, there should be a ":" between the + # field name and the field value and also the field value should not + # start with "{" or "<" which indicates the beginning of a message body. + # If there is no ":" or there is a "{" or "<" after ":", this field has + # to be a message or the input is ill-formed. + if tokenizer.TryConsume(':') and not tokenizer.LookingAt( + '{') and not tokenizer.LookingAt('<'): + _SkipFieldValue(tokenizer) + else: + _SkipFieldMessage(tokenizer) + + +def _SkipField(tokenizer): + """Skips over a complete field (name and value/message). + + Args: + tokenizer: A tokenizer to parse the field name and values. + """ + if tokenizer.TryConsume('['): + # Consume extension name. + tokenizer.ConsumeIdentifier() + while tokenizer.TryConsume('.'): + tokenizer.ConsumeIdentifier() + tokenizer.Consume(']') + else: + tokenizer.ConsumeIdentifierOrNumber() + + _SkipFieldContents(tokenizer) + + # For historical reasons, fields may optionally be separated by commas or + # semicolons. + if not tokenizer.TryConsume(','): + tokenizer.TryConsume(';') + + +def _SkipFieldMessage(tokenizer): + """Skips over a field message. + + Args: + tokenizer: A tokenizer to parse the field name and values. + """ + + if tokenizer.TryConsume('<'): + delimiter = '>' + else: + tokenizer.Consume('{') + delimiter = '}' + + while not tokenizer.LookingAt('>') and not tokenizer.LookingAt('}'): + _SkipField(tokenizer) + + tokenizer.Consume(delimiter) + + +def _SkipFieldValue(tokenizer): + """Skips over a field value. + + Args: + tokenizer: A tokenizer to parse the field name and values. + + Raises: + ParseError: In case an invalid field value is found. + """ + # String/bytes tokens can come in multiple adjacent string literals. + # If we can consume one, consume as many as we can. + if tokenizer.TryConsumeByteString(): + while tokenizer.TryConsumeByteString(): + pass + return + + if (not tokenizer.TryConsumeIdentifier() and + not _TryConsumeInt64(tokenizer) and not _TryConsumeUint64(tokenizer) and + not tokenizer.TryConsumeFloat()): + raise ParseError('Invalid field value: ' + tokenizer.token) + + +class Tokenizer(object): + """Protocol buffer text representation tokenizer. + + This class handles the lower level string parsing by splitting it into + meaningful tokens. + + It was directly ported from the Java protocol buffer API. + """ + + _WHITESPACE = re.compile(r'\s+') + _COMMENT = re.compile(r'(\s*#.*$)', re.MULTILINE) + _WHITESPACE_OR_COMMENT = re.compile(r'(\s|(#.*$))+', re.MULTILINE) + _TOKEN = re.compile('|'.join([ + r'[a-zA-Z_][0-9a-zA-Z_+-]*', # an identifier + r'([0-9+-]|(\.[0-9]))[0-9a-zA-Z_.+-]*', # a number + ] + [ # quoted str for each quote mark + # Avoid backtracking! https://stackoverflow.com/a/844267 + r'{qt}[^{qt}\n\\]*((\\.)+[^{qt}\n\\]*)*({qt}|\\?$)'.format(qt=mark) + for mark in _QUOTES + ])) + + _IDENTIFIER = re.compile(r'[^\d\W]\w*') + _IDENTIFIER_OR_NUMBER = re.compile(r'\w+') + + def __init__(self, lines, skip_comments=True): + self._position = 0 + self._line = -1 + self._column = 0 + self._token_start = None + self.token = '' + self._lines = iter(lines) + self._current_line = '' + self._previous_line = 0 + self._previous_column = 0 + self._more_lines = True + self._skip_comments = skip_comments + self._whitespace_pattern = (skip_comments and self._WHITESPACE_OR_COMMENT + or self._WHITESPACE) + self._SkipWhitespace() + self.NextToken() + + def LookingAt(self, token): + return self.token == token + + def AtEnd(self): + """Checks the end of the text was reached. + + Returns: + True iff the end was reached. + """ + return not self.token + + def _PopLine(self): + while len(self._current_line) <= self._column: + try: + self._current_line = next(self._lines) + except StopIteration: + self._current_line = '' + self._more_lines = False + return + else: + self._line += 1 + self._column = 0 + + def _SkipWhitespace(self): + while True: + self._PopLine() + match = self._whitespace_pattern.match(self._current_line, self._column) + if not match: + break + length = len(match.group(0)) + self._column += length + + def TryConsume(self, token): + """Tries to consume a given piece of text. + + Args: + token: Text to consume. + + Returns: + True iff the text was consumed. + """ + if self.token == token: + self.NextToken() + return True + return False + + def Consume(self, token): + """Consumes a piece of text. + + Args: + token: Text to consume. + + Raises: + ParseError: If the text couldn't be consumed. + """ + if not self.TryConsume(token): + raise self.ParseError('Expected "%s".' % token) + + def ConsumeComment(self): + result = self.token + if not self._COMMENT.match(result): + raise self.ParseError('Expected comment.') + self.NextToken() + return result + + def ConsumeCommentOrTrailingComment(self): + """Consumes a comment, returns a 2-tuple (trailing bool, comment str).""" + + # Tokenizer initializes _previous_line and _previous_column to 0. As the + # tokenizer starts, it looks like there is a previous token on the line. + just_started = self._line == 0 and self._column == 0 + + before_parsing = self._previous_line + comment = self.ConsumeComment() + + # A trailing comment is a comment on the same line than the previous token. + trailing = (self._previous_line == before_parsing + and not just_started) + + return trailing, comment + + def TryConsumeIdentifier(self): + try: + self.ConsumeIdentifier() + return True + except ParseError: + return False + + def ConsumeIdentifier(self): + """Consumes protocol message field identifier. + + Returns: + Identifier string. + + Raises: + ParseError: If an identifier couldn't be consumed. + """ + result = self.token + if not self._IDENTIFIER.match(result): + raise self.ParseError('Expected identifier.') + self.NextToken() + return result + + def TryConsumeIdentifierOrNumber(self): + try: + self.ConsumeIdentifierOrNumber() + return True + except ParseError: + return False + + def ConsumeIdentifierOrNumber(self): + """Consumes protocol message field identifier. + + Returns: + Identifier string. + + Raises: + ParseError: If an identifier couldn't be consumed. + """ + result = self.token + if not self._IDENTIFIER_OR_NUMBER.match(result): + raise self.ParseError('Expected identifier or number, got %s.' % result) + self.NextToken() + return result + + def TryConsumeInteger(self): + try: + self.ConsumeInteger() + return True + except ParseError: + return False + + def ConsumeInteger(self): + """Consumes an integer number. + + Returns: + The integer parsed. + + Raises: + ParseError: If an integer couldn't be consumed. + """ + try: + result = _ParseAbstractInteger(self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def TryConsumeFloat(self): + try: + self.ConsumeFloat() + return True + except ParseError: + return False + + def ConsumeFloat(self): + """Consumes an floating point number. + + Returns: + The number parsed. + + Raises: + ParseError: If a floating point number couldn't be consumed. + """ + try: + result = ParseFloat(self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def ConsumeBool(self): + """Consumes a boolean value. + + Returns: + The bool parsed. + + Raises: + ParseError: If a boolean value couldn't be consumed. + """ + try: + result = ParseBool(self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def TryConsumeByteString(self): + try: + self.ConsumeByteString() + return True + except ParseError: + return False + + def ConsumeString(self): + """Consumes a string value. + + Returns: + The string parsed. + + Raises: + ParseError: If a string value couldn't be consumed. + """ + the_bytes = self.ConsumeByteString() + try: + return str(the_bytes, 'utf-8') + except UnicodeDecodeError as e: + raise self._StringParseError(e) + + def ConsumeByteString(self): + """Consumes a byte array value. + + Returns: + The array parsed (as a string). + + Raises: + ParseError: If a byte array value couldn't be consumed. + """ + the_list = [self._ConsumeSingleByteString()] + while self.token and self.token[0] in _QUOTES: + the_list.append(self._ConsumeSingleByteString()) + return b''.join(the_list) + + def _ConsumeSingleByteString(self): + """Consume one token of a string literal. + + String literals (whether bytes or text) can come in multiple adjacent + tokens which are automatically concatenated, like in C or Python. This + method only consumes one token. + + Returns: + The token parsed. + Raises: + ParseError: When the wrong format data is found. + """ + text = self.token + if len(text) < 1 or text[0] not in _QUOTES: + raise self.ParseError('Expected string but found: %r' % (text,)) + + if len(text) < 2 or text[-1] != text[0]: + raise self.ParseError('String missing ending quote: %r' % (text,)) + + try: + result = text_encoding.CUnescape(text[1:-1]) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def ConsumeEnum(self, field): + try: + result = ParseEnum(field, self.token) + except ValueError as e: + raise self.ParseError(str(e)) + self.NextToken() + return result + + def ParseErrorPreviousToken(self, message): + """Creates and *returns* a ParseError for the previously read token. + + Args: + message: A message to set for the exception. + + Returns: + A ParseError instance. + """ + return ParseError(message, self._previous_line + 1, + self._previous_column + 1) + + def ParseError(self, message): + """Creates and *returns* a ParseError for the current token.""" + return ParseError('\'' + self._current_line + '\': ' + message, + self._line + 1, self._column + 1) + + def _StringParseError(self, e): + return self.ParseError('Couldn\'t parse string: ' + str(e)) + + def NextToken(self): + """Reads the next meaningful token.""" + self._previous_line = self._line + self._previous_column = self._column + + self._column += len(self.token) + self._SkipWhitespace() + + if not self._more_lines: + self.token = '' + return + + match = self._TOKEN.match(self._current_line, self._column) + if not match and not self._skip_comments: + match = self._COMMENT.match(self._current_line, self._column) + if match: + token = match.group(0) + self.token = token + else: + self.token = self._current_line[self._column] + +# Aliased so it can still be accessed by current visibility violators. +# TODO(dbarnett): Migrate violators to textformat_tokenizer. +_Tokenizer = Tokenizer # pylint: disable=invalid-name + + +def _ConsumeInt32(tokenizer): + """Consumes a signed 32bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If a signed 32bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=True, is_long=False) + + +def _ConsumeUint32(tokenizer): + """Consumes an unsigned 32bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If an unsigned 32bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=False, is_long=False) + + +def _TryConsumeInt64(tokenizer): + try: + _ConsumeInt64(tokenizer) + return True + except ParseError: + return False + + +def _ConsumeInt64(tokenizer): + """Consumes a signed 32bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If a signed 32bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=True, is_long=True) + + +def _TryConsumeUint64(tokenizer): + try: + _ConsumeUint64(tokenizer) + return True + except ParseError: + return False + + +def _ConsumeUint64(tokenizer): + """Consumes an unsigned 64bit integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + + Returns: + The integer parsed. + + Raises: + ParseError: If an unsigned 64bit integer couldn't be consumed. + """ + return _ConsumeInteger(tokenizer, is_signed=False, is_long=True) + + +def _ConsumeInteger(tokenizer, is_signed=False, is_long=False): + """Consumes an integer number from tokenizer. + + Args: + tokenizer: A tokenizer used to parse the number. + is_signed: True if a signed integer must be parsed. + is_long: True if a long integer must be parsed. + + Returns: + The integer parsed. + + Raises: + ParseError: If an integer with given characteristics couldn't be consumed. + """ + try: + result = ParseInteger(tokenizer.token, is_signed=is_signed, is_long=is_long) + except ValueError as e: + raise tokenizer.ParseError(str(e)) + tokenizer.NextToken() + return result + + +def ParseInteger(text, is_signed=False, is_long=False): + """Parses an integer. + + Args: + text: The text to parse. + is_signed: True if a signed integer must be parsed. + is_long: True if a long integer must be parsed. + + Returns: + The integer value. + + Raises: + ValueError: Thrown Iff the text is not a valid integer. + """ + # Do the actual parsing. Exception handling is propagated to caller. + result = _ParseAbstractInteger(text) + + # Check if the integer is sane. Exceptions handled by callers. + checker = _INTEGER_CHECKERS[2 * int(is_long) + int(is_signed)] + checker.CheckValue(result) + return result + + +def _ParseAbstractInteger(text): + """Parses an integer without checking size/signedness. + + Args: + text: The text to parse. + + Returns: + The integer value. + + Raises: + ValueError: Thrown Iff the text is not a valid integer. + """ + # Do the actual parsing. Exception handling is propagated to caller. + orig_text = text + c_octal_match = re.match(r'(-?)0(\d+)$', text) + if c_octal_match: + # Python 3 no longer supports 0755 octal syntax without the 'o', so + # we always use the '0o' prefix for multi-digit numbers starting with 0. + text = c_octal_match.group(1) + '0o' + c_octal_match.group(2) + try: + return int(text, 0) + except ValueError: + raise ValueError('Couldn\'t parse integer: %s' % orig_text) + + +def ParseFloat(text): + """Parse a floating point number. + + Args: + text: Text to parse. + + Returns: + The number parsed. + + Raises: + ValueError: If a floating point number couldn't be parsed. + """ + try: + # Assume Python compatible syntax. + return float(text) + except ValueError: + # Check alternative spellings. + if _FLOAT_INFINITY.match(text): + if text[0] == '-': + return float('-inf') + else: + return float('inf') + elif _FLOAT_NAN.match(text): + return float('nan') + else: + # assume '1.0f' format + try: + return float(text.rstrip('f')) + except ValueError: + raise ValueError('Couldn\'t parse float: %s' % text) + + +def ParseBool(text): + """Parse a boolean value. + + Args: + text: Text to parse. + + Returns: + Boolean values parsed + + Raises: + ValueError: If text is not a valid boolean. + """ + if text in ('true', 't', '1', 'True'): + return True + elif text in ('false', 'f', '0', 'False'): + return False + else: + raise ValueError('Expected "true" or "false".') + + +def ParseEnum(field, value): + """Parse an enum value. + + The value can be specified by a number (the enum value), or by + a string literal (the enum name). + + Args: + field: Enum field descriptor. + value: String value. + + Returns: + Enum value number. + + Raises: + ValueError: If the enum value could not be parsed. + """ + enum_descriptor = field.enum_type + try: + number = int(value, 0) + except ValueError: + # Identifier. + enum_value = enum_descriptor.values_by_name.get(value, None) + if enum_value is None: + raise ValueError('Enum type "%s" has no value named %s.' % + (enum_descriptor.full_name, value)) + else: + # Numeric value. + if hasattr(field.file, 'syntax'): + # Attribute is checked for compatibility. + if field.file.syntax == 'proto3': + # Proto3 accept numeric unknown enums. + return number + enum_value = enum_descriptor.values_by_number.get(number, None) + if enum_value is None: + raise ValueError('Enum type "%s" has no value with number %d.' % + (enum_descriptor.full_name, number)) + return enum_value.number diff --git a/lib/protobuf/timestamp_pb2.py b/lib/protobuf/timestamp_pb2.py new file mode 100644 index 0000000..558d496 --- /dev/null +++ b/lib/protobuf/timestamp_pb2.py @@ -0,0 +1,26 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/timestamp.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\x1fgoogle/protobuf/timestamp.proto\x12\x0fgoogle.protobuf\"+\n\tTimestamp\x12\x0f\n\x07seconds\x18\x01 \x01(\x03\x12\r\n\x05nanos\x18\x02 \x01(\x05\x42\x85\x01\n\x13\x63om.google.protobufB\x0eTimestampProtoP\x01Z2google.golang.org/protobuf/types/known/timestamppb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.timestamp_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\023com.google.protobufB\016TimestampProtoP\001Z2google.golang.org/protobuf/types/known/timestamppb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _TIMESTAMP._serialized_start=52 + _TIMESTAMP._serialized_end=95 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/type_pb2.py b/lib/protobuf/type_pb2.py new file mode 100644 index 0000000..19903fb --- /dev/null +++ b/lib/protobuf/type_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/type.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 google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import source_context_pb2 as google_dot_protobuf_dot_source__context__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n\x1agoogle/protobuf/type.proto\x12\x0fgoogle.protobuf\x1a\x19google/protobuf/any.proto\x1a$google/protobuf/source_context.proto\"\xd7\x01\n\x04Type\x12\x0c\n\x04name\x18\x01 \x01(\t\x12&\n\x06\x66ields\x18\x02 \x03(\x0b\x32\x16.google.protobuf.Field\x12\x0e\n\x06oneofs\x18\x03 \x03(\t\x12(\n\x07options\x18\x04 \x03(\x0b\x32\x17.google.protobuf.Option\x12\x36\n\x0esource_context\x18\x05 \x01(\x0b\x32\x1e.google.protobuf.SourceContext\x12\'\n\x06syntax\x18\x06 \x01(\x0e\x32\x17.google.protobuf.Syntax\"\xd5\x05\n\x05\x46ield\x12)\n\x04kind\x18\x01 \x01(\x0e\x32\x1b.google.protobuf.Field.Kind\x12\x37\n\x0b\x63\x61rdinality\x18\x02 \x01(\x0e\x32\".google.protobuf.Field.Cardinality\x12\x0e\n\x06number\x18\x03 \x01(\x05\x12\x0c\n\x04name\x18\x04 \x01(\t\x12\x10\n\x08type_url\x18\x06 \x01(\t\x12\x13\n\x0boneof_index\x18\x07 \x01(\x05\x12\x0e\n\x06packed\x18\x08 \x01(\x08\x12(\n\x07options\x18\t \x03(\x0b\x32\x17.google.protobuf.Option\x12\x11\n\tjson_name\x18\n \x01(\t\x12\x15\n\rdefault_value\x18\x0b \x01(\t\"\xc8\x02\n\x04Kind\x12\x10\n\x0cTYPE_UNKNOWN\x10\x00\x12\x0f\n\x0bTYPE_DOUBLE\x10\x01\x12\x0e\n\nTYPE_FLOAT\x10\x02\x12\x0e\n\nTYPE_INT64\x10\x03\x12\x0f\n\x0bTYPE_UINT64\x10\x04\x12\x0e\n\nTYPE_INT32\x10\x05\x12\x10\n\x0cTYPE_FIXED64\x10\x06\x12\x10\n\x0cTYPE_FIXED32\x10\x07\x12\r\n\tTYPE_BOOL\x10\x08\x12\x0f\n\x0bTYPE_STRING\x10\t\x12\x0e\n\nTYPE_GROUP\x10\n\x12\x10\n\x0cTYPE_MESSAGE\x10\x0b\x12\x0e\n\nTYPE_BYTES\x10\x0c\x12\x0f\n\x0bTYPE_UINT32\x10\r\x12\r\n\tTYPE_ENUM\x10\x0e\x12\x11\n\rTYPE_SFIXED32\x10\x0f\x12\x11\n\rTYPE_SFIXED64\x10\x10\x12\x0f\n\x0bTYPE_SINT32\x10\x11\x12\x0f\n\x0bTYPE_SINT64\x10\x12\"t\n\x0b\x43\x61rdinality\x12\x17\n\x13\x43\x41RDINALITY_UNKNOWN\x10\x00\x12\x18\n\x14\x43\x41RDINALITY_OPTIONAL\x10\x01\x12\x18\n\x14\x43\x41RDINALITY_REQUIRED\x10\x02\x12\x18\n\x14\x43\x41RDINALITY_REPEATED\x10\x03\"\xce\x01\n\x04\x45num\x12\x0c\n\x04name\x18\x01 \x01(\t\x12-\n\tenumvalue\x18\x02 \x03(\x0b\x32\x1a.google.protobuf.EnumValue\x12(\n\x07options\x18\x03 \x03(\x0b\x32\x17.google.protobuf.Option\x12\x36\n\x0esource_context\x18\x04 \x01(\x0b\x32\x1e.google.protobuf.SourceContext\x12\'\n\x06syntax\x18\x05 \x01(\x0e\x32\x17.google.protobuf.Syntax\"S\n\tEnumValue\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x0e\n\x06number\x18\x02 \x01(\x05\x12(\n\x07options\x18\x03 \x03(\x0b\x32\x17.google.protobuf.Option\";\n\x06Option\x12\x0c\n\x04name\x18\x01 \x01(\t\x12#\n\x05value\x18\x02 \x01(\x0b\x32\x14.google.protobuf.Any*.\n\x06Syntax\x12\x11\n\rSYNTAX_PROTO2\x10\x00\x12\x11\n\rSYNTAX_PROTO3\x10\x01\x42{\n\x13\x63om.google.protobufB\tTypeProtoP\x01Z-google.golang.org/protobuf/types/known/typepb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.type_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\023com.google.protobufB\tTypeProtoP\001Z-google.golang.org/protobuf/types/known/typepb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _SYNTAX._serialized_start=1413 + _SYNTAX._serialized_end=1459 + _TYPE._serialized_start=113 + _TYPE._serialized_end=328 + _FIELD._serialized_start=331 + _FIELD._serialized_end=1056 + _FIELD_KIND._serialized_start=610 + _FIELD_KIND._serialized_end=938 + _FIELD_CARDINALITY._serialized_start=940 + _FIELD_CARDINALITY._serialized_end=1056 + _ENUM._serialized_start=1059 + _ENUM._serialized_end=1265 + _ENUMVALUE._serialized_start=1267 + _ENUMVALUE._serialized_end=1350 + _OPTION._serialized_start=1352 + _OPTION._serialized_end=1411 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/util/__init__.py b/lib/protobuf/util/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/lib/protobuf/util/json_format_pb2.py b/lib/protobuf/util/json_format_pb2.py new file mode 100644 index 0000000..66a5836 --- /dev/null +++ b/lib/protobuf/util/json_format_pb2.py @@ -0,0 +1,72 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/util/json_format.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&google/protobuf/util/json_format.proto\x12\x11protobuf_unittest\"\x89\x01\n\x13TestFlagsAndStrings\x12\t\n\x01\x41\x18\x01 \x02(\x05\x12K\n\rrepeatedgroup\x18\x02 \x03(\n24.protobuf_unittest.TestFlagsAndStrings.RepeatedGroup\x1a\x1a\n\rRepeatedGroup\x12\t\n\x01\x66\x18\x03 \x02(\t\"!\n\x14TestBase64ByteArrays\x12\t\n\x01\x61\x18\x01 \x02(\x0c\"G\n\x12TestJavaScriptJSON\x12\t\n\x01\x61\x18\x01 \x01(\x05\x12\r\n\x05\x66inal\x18\x02 \x01(\x02\x12\n\n\x02in\x18\x03 \x01(\t\x12\x0b\n\x03Var\x18\x04 \x01(\t\"Q\n\x18TestJavaScriptOrderJSON1\x12\t\n\x01\x64\x18\x01 \x01(\x05\x12\t\n\x01\x63\x18\x02 \x01(\x05\x12\t\n\x01x\x18\x03 \x01(\x08\x12\t\n\x01\x62\x18\x04 \x01(\x05\x12\t\n\x01\x61\x18\x05 \x01(\x05\"\x89\x01\n\x18TestJavaScriptOrderJSON2\x12\t\n\x01\x64\x18\x01 \x01(\x05\x12\t\n\x01\x63\x18\x02 \x01(\x05\x12\t\n\x01x\x18\x03 \x01(\x08\x12\t\n\x01\x62\x18\x04 \x01(\x05\x12\t\n\x01\x61\x18\x05 \x01(\x05\x12\x36\n\x01z\x18\x06 \x03(\x0b\x32+.protobuf_unittest.TestJavaScriptOrderJSON1\"$\n\x0cTestLargeInt\x12\t\n\x01\x61\x18\x01 \x02(\x03\x12\t\n\x01\x62\x18\x02 \x02(\x04\"\xa0\x01\n\x0bTestNumbers\x12\x30\n\x01\x61\x18\x01 \x01(\x0e\x32%.protobuf_unittest.TestNumbers.MyType\x12\t\n\x01\x62\x18\x02 \x01(\x05\x12\t\n\x01\x63\x18\x03 \x01(\x02\x12\t\n\x01\x64\x18\x04 \x01(\x08\x12\t\n\x01\x65\x18\x05 \x01(\x01\x12\t\n\x01\x66\x18\x06 \x01(\r\"(\n\x06MyType\x12\x06\n\x02OK\x10\x00\x12\x0b\n\x07WARNING\x10\x01\x12\t\n\x05\x45RROR\x10\x02\"T\n\rTestCamelCase\x12\x14\n\x0cnormal_field\x18\x01 \x01(\t\x12\x15\n\rCAPITAL_FIELD\x18\x02 \x01(\x05\x12\x16\n\x0e\x43\x61melCaseField\x18\x03 \x01(\x05\"|\n\x0bTestBoolMap\x12=\n\x08\x62ool_map\x18\x01 \x03(\x0b\x32+.protobuf_unittest.TestBoolMap.BoolMapEntry\x1a.\n\x0c\x42oolMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\x08\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"O\n\rTestRecursion\x12\r\n\x05value\x18\x01 \x01(\x05\x12/\n\x05\x63hild\x18\x02 \x01(\x0b\x32 .protobuf_unittest.TestRecursion\"\x86\x01\n\rTestStringMap\x12\x43\n\nstring_map\x18\x01 \x03(\x0b\x32/.protobuf_unittest.TestStringMap.StringMapEntry\x1a\x30\n\x0eStringMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xc4\x01\n\x14TestStringSerializer\x12\x15\n\rscalar_string\x18\x01 \x01(\t\x12\x17\n\x0frepeated_string\x18\x02 \x03(\t\x12J\n\nstring_map\x18\x03 \x03(\x0b\x32\x36.protobuf_unittest.TestStringSerializer.StringMapEntry\x1a\x30\n\x0eStringMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"$\n\x18TestMessageWithExtension*\x08\x08\x64\x10\x80\x80\x80\x80\x02\"z\n\rTestExtension\x12\r\n\x05value\x18\x01 \x01(\t2Z\n\x03\x65xt\x12+.protobuf_unittest.TestMessageWithExtension\x18\x64 \x01(\x0b\x32 .protobuf_unittest.TestExtension\"Q\n\x14TestDefaultEnumValue\x12\x39\n\nenum_value\x18\x01 \x01(\x0e\x32\x1c.protobuf_unittest.EnumValue:\x07\x44\x45\x46\x41ULT*2\n\tEnumValue\x12\x0c\n\x08PROTOCOL\x10\x00\x12\n\n\x06\x42UFFER\x10\x01\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x02') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.util.json_format_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + TestMessageWithExtension.RegisterExtension(_TESTEXTENSION.extensions_by_name['ext']) + + DESCRIPTOR._options = None + _TESTBOOLMAP_BOOLMAPENTRY._options = None + _TESTBOOLMAP_BOOLMAPENTRY._serialized_options = b'8\001' + _TESTSTRINGMAP_STRINGMAPENTRY._options = None + _TESTSTRINGMAP_STRINGMAPENTRY._serialized_options = b'8\001' + _TESTSTRINGSERIALIZER_STRINGMAPENTRY._options = None + _TESTSTRINGSERIALIZER_STRINGMAPENTRY._serialized_options = b'8\001' + _ENUMVALUE._serialized_start=1607 + _ENUMVALUE._serialized_end=1657 + _TESTFLAGSANDSTRINGS._serialized_start=62 + _TESTFLAGSANDSTRINGS._serialized_end=199 + _TESTFLAGSANDSTRINGS_REPEATEDGROUP._serialized_start=173 + _TESTFLAGSANDSTRINGS_REPEATEDGROUP._serialized_end=199 + _TESTBASE64BYTEARRAYS._serialized_start=201 + _TESTBASE64BYTEARRAYS._serialized_end=234 + _TESTJAVASCRIPTJSON._serialized_start=236 + _TESTJAVASCRIPTJSON._serialized_end=307 + _TESTJAVASCRIPTORDERJSON1._serialized_start=309 + _TESTJAVASCRIPTORDERJSON1._serialized_end=390 + _TESTJAVASCRIPTORDERJSON2._serialized_start=393 + _TESTJAVASCRIPTORDERJSON2._serialized_end=530 + _TESTLARGEINT._serialized_start=532 + _TESTLARGEINT._serialized_end=568 + _TESTNUMBERS._serialized_start=571 + _TESTNUMBERS._serialized_end=731 + _TESTNUMBERS_MYTYPE._serialized_start=691 + _TESTNUMBERS_MYTYPE._serialized_end=731 + _TESTCAMELCASE._serialized_start=733 + _TESTCAMELCASE._serialized_end=817 + _TESTBOOLMAP._serialized_start=819 + _TESTBOOLMAP._serialized_end=943 + _TESTBOOLMAP_BOOLMAPENTRY._serialized_start=897 + _TESTBOOLMAP_BOOLMAPENTRY._serialized_end=943 + _TESTRECURSION._serialized_start=945 + _TESTRECURSION._serialized_end=1024 + _TESTSTRINGMAP._serialized_start=1027 + _TESTSTRINGMAP._serialized_end=1161 + _TESTSTRINGMAP_STRINGMAPENTRY._serialized_start=1113 + _TESTSTRINGMAP_STRINGMAPENTRY._serialized_end=1161 + _TESTSTRINGSERIALIZER._serialized_start=1164 + _TESTSTRINGSERIALIZER._serialized_end=1360 + _TESTSTRINGSERIALIZER_STRINGMAPENTRY._serialized_start=1113 + _TESTSTRINGSERIALIZER_STRINGMAPENTRY._serialized_end=1161 + _TESTMESSAGEWITHEXTENSION._serialized_start=1362 + _TESTMESSAGEWITHEXTENSION._serialized_end=1398 + _TESTEXTENSION._serialized_start=1400 + _TESTEXTENSION._serialized_end=1522 + _TESTDEFAULTENUMVALUE._serialized_start=1524 + _TESTDEFAULTENUMVALUE._serialized_end=1605 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/util/json_format_proto3_pb2.py b/lib/protobuf/util/json_format_proto3_pb2.py new file mode 100644 index 0000000..5498dea --- /dev/null +++ b/lib/protobuf/util/json_format_proto3_pb2.py @@ -0,0 +1,129 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/util/json_format_proto3.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 google.protobuf import any_pb2 as google_dot_protobuf_dot_any__pb2 +from google.protobuf import duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import field_mask_pb2 as google_dot_protobuf_dot_field__mask__pb2 +from google.protobuf import struct_pb2 as google_dot_protobuf_dot_struct__pb2 +from google.protobuf import timestamp_pb2 as google_dot_protobuf_dot_timestamp__pb2 +from google.protobuf import wrappers_pb2 as google_dot_protobuf_dot_wrappers__pb2 +from google.protobuf import unittest_pb2 as google_dot_protobuf_dot_unittest__pb2 + + +DESCRIPTOR = _descriptor_pool.Default().AddSerializedFile(b'\n-google/protobuf/util/json_format_proto3.proto\x12\x06proto3\x1a\x19google/protobuf/any.proto\x1a\x1egoogle/protobuf/duration.proto\x1a google/protobuf/field_mask.proto\x1a\x1cgoogle/protobuf/struct.proto\x1a\x1fgoogle/protobuf/timestamp.proto\x1a\x1egoogle/protobuf/wrappers.proto\x1a\x1egoogle/protobuf/unittest.proto\"\x1c\n\x0bMessageType\x12\r\n\x05value\x18\x01 \x01(\x05\"\x94\x05\n\x0bTestMessage\x12\x12\n\nbool_value\x18\x01 \x01(\x08\x12\x13\n\x0bint32_value\x18\x02 \x01(\x05\x12\x13\n\x0bint64_value\x18\x03 \x01(\x03\x12\x14\n\x0cuint32_value\x18\x04 \x01(\r\x12\x14\n\x0cuint64_value\x18\x05 \x01(\x04\x12\x13\n\x0b\x66loat_value\x18\x06 \x01(\x02\x12\x14\n\x0c\x64ouble_value\x18\x07 \x01(\x01\x12\x14\n\x0cstring_value\x18\x08 \x01(\t\x12\x13\n\x0b\x62ytes_value\x18\t \x01(\x0c\x12$\n\nenum_value\x18\n \x01(\x0e\x32\x10.proto3.EnumType\x12*\n\rmessage_value\x18\x0b \x01(\x0b\x32\x13.proto3.MessageType\x12\x1b\n\x13repeated_bool_value\x18\x15 \x03(\x08\x12\x1c\n\x14repeated_int32_value\x18\x16 \x03(\x05\x12\x1c\n\x14repeated_int64_value\x18\x17 \x03(\x03\x12\x1d\n\x15repeated_uint32_value\x18\x18 \x03(\r\x12\x1d\n\x15repeated_uint64_value\x18\x19 \x03(\x04\x12\x1c\n\x14repeated_float_value\x18\x1a \x03(\x02\x12\x1d\n\x15repeated_double_value\x18\x1b \x03(\x01\x12\x1d\n\x15repeated_string_value\x18\x1c \x03(\t\x12\x1c\n\x14repeated_bytes_value\x18\x1d \x03(\x0c\x12-\n\x13repeated_enum_value\x18\x1e \x03(\x0e\x32\x10.proto3.EnumType\x12\x33\n\x16repeated_message_value\x18\x1f \x03(\x0b\x32\x13.proto3.MessageType\"\x8c\x02\n\tTestOneof\x12\x1b\n\x11oneof_int32_value\x18\x01 \x01(\x05H\x00\x12\x1c\n\x12oneof_string_value\x18\x02 \x01(\tH\x00\x12\x1b\n\x11oneof_bytes_value\x18\x03 \x01(\x0cH\x00\x12,\n\x10oneof_enum_value\x18\x04 \x01(\x0e\x32\x10.proto3.EnumTypeH\x00\x12\x32\n\x13oneof_message_value\x18\x05 \x01(\x0b\x32\x13.proto3.MessageTypeH\x00\x12\x36\n\x10oneof_null_value\x18\x06 \x01(\x0e\x32\x1a.google.protobuf.NullValueH\x00\x42\r\n\x0boneof_value\"\xe1\x04\n\x07TestMap\x12.\n\x08\x62ool_map\x18\x01 \x03(\x0b\x32\x1c.proto3.TestMap.BoolMapEntry\x12\x30\n\tint32_map\x18\x02 \x03(\x0b\x32\x1d.proto3.TestMap.Int32MapEntry\x12\x30\n\tint64_map\x18\x03 \x03(\x0b\x32\x1d.proto3.TestMap.Int64MapEntry\x12\x32\n\nuint32_map\x18\x04 \x03(\x0b\x32\x1e.proto3.TestMap.Uint32MapEntry\x12\x32\n\nuint64_map\x18\x05 \x03(\x0b\x32\x1e.proto3.TestMap.Uint64MapEntry\x12\x32\n\nstring_map\x18\x06 \x03(\x0b\x32\x1e.proto3.TestMap.StringMapEntry\x1a.\n\x0c\x42oolMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\x08\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a/\n\rInt32MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a/\n\rInt64MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\x03\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a\x30\n\x0eUint32MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\r\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a\x30\n\x0eUint64MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a\x30\n\x0eStringMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"\x85\x06\n\rTestNestedMap\x12\x34\n\x08\x62ool_map\x18\x01 \x03(\x0b\x32\".proto3.TestNestedMap.BoolMapEntry\x12\x36\n\tint32_map\x18\x02 \x03(\x0b\x32#.proto3.TestNestedMap.Int32MapEntry\x12\x36\n\tint64_map\x18\x03 \x03(\x0b\x32#.proto3.TestNestedMap.Int64MapEntry\x12\x38\n\nuint32_map\x18\x04 \x03(\x0b\x32$.proto3.TestNestedMap.Uint32MapEntry\x12\x38\n\nuint64_map\x18\x05 \x03(\x0b\x32$.proto3.TestNestedMap.Uint64MapEntry\x12\x38\n\nstring_map\x18\x06 \x03(\x0b\x32$.proto3.TestNestedMap.StringMapEntry\x12\x32\n\x07map_map\x18\x07 \x03(\x0b\x32!.proto3.TestNestedMap.MapMapEntry\x1a.\n\x0c\x42oolMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\x08\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a/\n\rInt32MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\x05\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a/\n\rInt64MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\x03\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a\x30\n\x0eUint32MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\r\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a\x30\n\x0eUint64MapEntry\x12\x0b\n\x03key\x18\x01 \x01(\x04\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a\x30\n\x0eStringMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\x1a\x44\n\x0bMapMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12$\n\x05value\x18\x02 \x01(\x0b\x32\x15.proto3.TestNestedMap:\x02\x38\x01\"{\n\rTestStringMap\x12\x38\n\nstring_map\x18\x01 \x03(\x0b\x32$.proto3.TestStringMap.StringMapEntry\x1a\x30\n\x0eStringMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xee\x07\n\x0bTestWrapper\x12.\n\nbool_value\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.BoolValue\x12\x30\n\x0bint32_value\x18\x02 \x01(\x0b\x32\x1b.google.protobuf.Int32Value\x12\x30\n\x0bint64_value\x18\x03 \x01(\x0b\x32\x1b.google.protobuf.Int64Value\x12\x32\n\x0cuint32_value\x18\x04 \x01(\x0b\x32\x1c.google.protobuf.UInt32Value\x12\x32\n\x0cuint64_value\x18\x05 \x01(\x0b\x32\x1c.google.protobuf.UInt64Value\x12\x30\n\x0b\x66loat_value\x18\x06 \x01(\x0b\x32\x1b.google.protobuf.FloatValue\x12\x32\n\x0c\x64ouble_value\x18\x07 \x01(\x0b\x32\x1c.google.protobuf.DoubleValue\x12\x32\n\x0cstring_value\x18\x08 \x01(\x0b\x32\x1c.google.protobuf.StringValue\x12\x30\n\x0b\x62ytes_value\x18\t \x01(\x0b\x32\x1b.google.protobuf.BytesValue\x12\x37\n\x13repeated_bool_value\x18\x0b \x03(\x0b\x32\x1a.google.protobuf.BoolValue\x12\x39\n\x14repeated_int32_value\x18\x0c \x03(\x0b\x32\x1b.google.protobuf.Int32Value\x12\x39\n\x14repeated_int64_value\x18\r \x03(\x0b\x32\x1b.google.protobuf.Int64Value\x12;\n\x15repeated_uint32_value\x18\x0e \x03(\x0b\x32\x1c.google.protobuf.UInt32Value\x12;\n\x15repeated_uint64_value\x18\x0f \x03(\x0b\x32\x1c.google.protobuf.UInt64Value\x12\x39\n\x14repeated_float_value\x18\x10 \x03(\x0b\x32\x1b.google.protobuf.FloatValue\x12;\n\x15repeated_double_value\x18\x11 \x03(\x0b\x32\x1c.google.protobuf.DoubleValue\x12;\n\x15repeated_string_value\x18\x12 \x03(\x0b\x32\x1c.google.protobuf.StringValue\x12\x39\n\x14repeated_bytes_value\x18\x13 \x03(\x0b\x32\x1b.google.protobuf.BytesValue\"n\n\rTestTimestamp\x12)\n\x05value\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.Timestamp\x12\x32\n\x0erepeated_value\x18\x02 \x03(\x0b\x32\x1a.google.protobuf.Timestamp\"k\n\x0cTestDuration\x12(\n\x05value\x18\x01 \x01(\x0b\x32\x19.google.protobuf.Duration\x12\x31\n\x0erepeated_value\x18\x02 \x03(\x0b\x32\x19.google.protobuf.Duration\":\n\rTestFieldMask\x12)\n\x05value\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.FieldMask\"e\n\nTestStruct\x12&\n\x05value\x18\x01 \x01(\x0b\x32\x17.google.protobuf.Struct\x12/\n\x0erepeated_value\x18\x02 \x03(\x0b\x32\x17.google.protobuf.Struct\"\\\n\x07TestAny\x12#\n\x05value\x18\x01 \x01(\x0b\x32\x14.google.protobuf.Any\x12,\n\x0erepeated_value\x18\x02 \x03(\x0b\x32\x14.google.protobuf.Any\"b\n\tTestValue\x12%\n\x05value\x18\x01 \x01(\x0b\x32\x16.google.protobuf.Value\x12.\n\x0erepeated_value\x18\x02 \x03(\x0b\x32\x16.google.protobuf.Value\"n\n\rTestListValue\x12)\n\x05value\x18\x01 \x01(\x0b\x32\x1a.google.protobuf.ListValue\x12\x32\n\x0erepeated_value\x18\x02 \x03(\x0b\x32\x1a.google.protobuf.ListValue\"\x89\x01\n\rTestBoolValue\x12\x12\n\nbool_value\x18\x01 \x01(\x08\x12\x34\n\x08\x62ool_map\x18\x02 \x03(\x0b\x32\".proto3.TestBoolValue.BoolMapEntry\x1a.\n\x0c\x42oolMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\x08\x12\r\n\x05value\x18\x02 \x01(\x05:\x02\x38\x01\"+\n\x12TestCustomJsonName\x12\x15\n\x05value\x18\x01 \x01(\x05R\x06@value\"J\n\x0eTestExtensions\x12\x38\n\nextensions\x18\x01 \x01(\x0b\x32$.protobuf_unittest.TestAllExtensions\"\x84\x01\n\rTestEnumValue\x12%\n\x0b\x65num_value1\x18\x01 \x01(\x0e\x32\x10.proto3.EnumType\x12%\n\x0b\x65num_value2\x18\x02 \x01(\x0e\x32\x10.proto3.EnumType\x12%\n\x0b\x65num_value3\x18\x03 \x01(\x0e\x32\x10.proto3.EnumType*\x1c\n\x08\x45numType\x12\x07\n\x03\x46OO\x10\x00\x12\x07\n\x03\x42\x41R\x10\x01\x42,\n\x18\x63om.google.protobuf.utilB\x10JsonFormatProto3b\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.util.json_format_proto3_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\030com.google.protobuf.utilB\020JsonFormatProto3' + _TESTMAP_BOOLMAPENTRY._options = None + _TESTMAP_BOOLMAPENTRY._serialized_options = b'8\001' + _TESTMAP_INT32MAPENTRY._options = None + _TESTMAP_INT32MAPENTRY._serialized_options = b'8\001' + _TESTMAP_INT64MAPENTRY._options = None + _TESTMAP_INT64MAPENTRY._serialized_options = b'8\001' + _TESTMAP_UINT32MAPENTRY._options = None + _TESTMAP_UINT32MAPENTRY._serialized_options = b'8\001' + _TESTMAP_UINT64MAPENTRY._options = None + _TESTMAP_UINT64MAPENTRY._serialized_options = b'8\001' + _TESTMAP_STRINGMAPENTRY._options = None + _TESTMAP_STRINGMAPENTRY._serialized_options = b'8\001' + _TESTNESTEDMAP_BOOLMAPENTRY._options = None + _TESTNESTEDMAP_BOOLMAPENTRY._serialized_options = b'8\001' + _TESTNESTEDMAP_INT32MAPENTRY._options = None + _TESTNESTEDMAP_INT32MAPENTRY._serialized_options = b'8\001' + _TESTNESTEDMAP_INT64MAPENTRY._options = None + _TESTNESTEDMAP_INT64MAPENTRY._serialized_options = b'8\001' + _TESTNESTEDMAP_UINT32MAPENTRY._options = None + _TESTNESTEDMAP_UINT32MAPENTRY._serialized_options = b'8\001' + _TESTNESTEDMAP_UINT64MAPENTRY._options = None + _TESTNESTEDMAP_UINT64MAPENTRY._serialized_options = b'8\001' + _TESTNESTEDMAP_STRINGMAPENTRY._options = None + _TESTNESTEDMAP_STRINGMAPENTRY._serialized_options = b'8\001' + _TESTNESTEDMAP_MAPMAPENTRY._options = None + _TESTNESTEDMAP_MAPMAPENTRY._serialized_options = b'8\001' + _TESTSTRINGMAP_STRINGMAPENTRY._options = None + _TESTSTRINGMAP_STRINGMAPENTRY._serialized_options = b'8\001' + _TESTBOOLVALUE_BOOLMAPENTRY._options = None + _TESTBOOLVALUE_BOOLMAPENTRY._serialized_options = b'8\001' + _ENUMTYPE._serialized_start=4849 + _ENUMTYPE._serialized_end=4877 + _MESSAGETYPE._serialized_start=277 + _MESSAGETYPE._serialized_end=305 + _TESTMESSAGE._serialized_start=308 + _TESTMESSAGE._serialized_end=968 + _TESTONEOF._serialized_start=971 + _TESTONEOF._serialized_end=1239 + _TESTMAP._serialized_start=1242 + _TESTMAP._serialized_end=1851 + _TESTMAP_BOOLMAPENTRY._serialized_start=1557 + _TESTMAP_BOOLMAPENTRY._serialized_end=1603 + _TESTMAP_INT32MAPENTRY._serialized_start=1605 + _TESTMAP_INT32MAPENTRY._serialized_end=1652 + _TESTMAP_INT64MAPENTRY._serialized_start=1654 + _TESTMAP_INT64MAPENTRY._serialized_end=1701 + _TESTMAP_UINT32MAPENTRY._serialized_start=1703 + _TESTMAP_UINT32MAPENTRY._serialized_end=1751 + _TESTMAP_UINT64MAPENTRY._serialized_start=1753 + _TESTMAP_UINT64MAPENTRY._serialized_end=1801 + _TESTMAP_STRINGMAPENTRY._serialized_start=1803 + _TESTMAP_STRINGMAPENTRY._serialized_end=1851 + _TESTNESTEDMAP._serialized_start=1854 + _TESTNESTEDMAP._serialized_end=2627 + _TESTNESTEDMAP_BOOLMAPENTRY._serialized_start=1557 + _TESTNESTEDMAP_BOOLMAPENTRY._serialized_end=1603 + _TESTNESTEDMAP_INT32MAPENTRY._serialized_start=1605 + _TESTNESTEDMAP_INT32MAPENTRY._serialized_end=1652 + _TESTNESTEDMAP_INT64MAPENTRY._serialized_start=1654 + _TESTNESTEDMAP_INT64MAPENTRY._serialized_end=1701 + _TESTNESTEDMAP_UINT32MAPENTRY._serialized_start=1703 + _TESTNESTEDMAP_UINT32MAPENTRY._serialized_end=1751 + _TESTNESTEDMAP_UINT64MAPENTRY._serialized_start=1753 + _TESTNESTEDMAP_UINT64MAPENTRY._serialized_end=1801 + _TESTNESTEDMAP_STRINGMAPENTRY._serialized_start=1803 + _TESTNESTEDMAP_STRINGMAPENTRY._serialized_end=1851 + _TESTNESTEDMAP_MAPMAPENTRY._serialized_start=2559 + _TESTNESTEDMAP_MAPMAPENTRY._serialized_end=2627 + _TESTSTRINGMAP._serialized_start=2629 + _TESTSTRINGMAP._serialized_end=2752 + _TESTSTRINGMAP_STRINGMAPENTRY._serialized_start=2704 + _TESTSTRINGMAP_STRINGMAPENTRY._serialized_end=2752 + _TESTWRAPPER._serialized_start=2755 + _TESTWRAPPER._serialized_end=3761 + _TESTTIMESTAMP._serialized_start=3763 + _TESTTIMESTAMP._serialized_end=3873 + _TESTDURATION._serialized_start=3875 + _TESTDURATION._serialized_end=3982 + _TESTFIELDMASK._serialized_start=3984 + _TESTFIELDMASK._serialized_end=4042 + _TESTSTRUCT._serialized_start=4044 + _TESTSTRUCT._serialized_end=4145 + _TESTANY._serialized_start=4147 + _TESTANY._serialized_end=4239 + _TESTVALUE._serialized_start=4241 + _TESTVALUE._serialized_end=4339 + _TESTLISTVALUE._serialized_start=4341 + _TESTLISTVALUE._serialized_end=4451 + _TESTBOOLVALUE._serialized_start=4454 + _TESTBOOLVALUE._serialized_end=4591 + _TESTBOOLVALUE_BOOLMAPENTRY._serialized_start=1557 + _TESTBOOLVALUE_BOOLMAPENTRY._serialized_end=1603 + _TESTCUSTOMJSONNAME._serialized_start=4593 + _TESTCUSTOMJSONNAME._serialized_end=4636 + _TESTEXTENSIONS._serialized_start=4638 + _TESTEXTENSIONS._serialized_end=4712 + _TESTENUMVALUE._serialized_start=4715 + _TESTENUMVALUE._serialized_end=4847 +# @@protoc_insertion_point(module_scope) diff --git a/lib/protobuf/wrappers_pb2.py b/lib/protobuf/wrappers_pb2.py new file mode 100644 index 0000000..e49eb4c --- /dev/null +++ b/lib/protobuf/wrappers_pb2.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: google/protobuf/wrappers.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\x1egoogle/protobuf/wrappers.proto\x12\x0fgoogle.protobuf\"\x1c\n\x0b\x44oubleValue\x12\r\n\x05value\x18\x01 \x01(\x01\"\x1b\n\nFloatValue\x12\r\n\x05value\x18\x01 \x01(\x02\"\x1b\n\nInt64Value\x12\r\n\x05value\x18\x01 \x01(\x03\"\x1c\n\x0bUInt64Value\x12\r\n\x05value\x18\x01 \x01(\x04\"\x1b\n\nInt32Value\x12\r\n\x05value\x18\x01 \x01(\x05\"\x1c\n\x0bUInt32Value\x12\r\n\x05value\x18\x01 \x01(\r\"\x1a\n\tBoolValue\x12\r\n\x05value\x18\x01 \x01(\x08\"\x1c\n\x0bStringValue\x12\r\n\x05value\x18\x01 \x01(\t\"\x1b\n\nBytesValue\x12\r\n\x05value\x18\x01 \x01(\x0c\x42\x83\x01\n\x13\x63om.google.protobufB\rWrappersProtoP\x01Z1google.golang.org/protobuf/types/known/wrapperspb\xf8\x01\x01\xa2\x02\x03GPB\xaa\x02\x1eGoogle.Protobuf.WellKnownTypesb\x06proto3') + +_builder.BuildMessageAndEnumDescriptors(DESCRIPTOR, globals()) +_builder.BuildTopDescriptorsAndMessages(DESCRIPTOR, 'google.protobuf.wrappers_pb2', globals()) +if _descriptor._USE_C_DESCRIPTORS == False: + + DESCRIPTOR._options = None + DESCRIPTOR._serialized_options = b'\n\023com.google.protobufB\rWrappersProtoP\001Z1google.golang.org/protobuf/types/known/wrapperspb\370\001\001\242\002\003GPB\252\002\036Google.Protobuf.WellKnownTypes' + _DOUBLEVALUE._serialized_start=51 + _DOUBLEVALUE._serialized_end=79 + _FLOATVALUE._serialized_start=81 + _FLOATVALUE._serialized_end=108 + _INT64VALUE._serialized_start=110 + _INT64VALUE._serialized_end=137 + _UINT64VALUE._serialized_start=139 + _UINT64VALUE._serialized_end=167 + _INT32VALUE._serialized_start=169 + _INT32VALUE._serialized_end=196 + _UINT32VALUE._serialized_start=198 + _UINT32VALUE._serialized_end=226 + _BOOLVALUE._serialized_start=228 + _BOOLVALUE._serialized_end=254 + _STRINGVALUE._serialized_start=256 + _STRINGVALUE._serialized_end=284 + _BYTESVALUE._serialized_start=286 + _BYTESVALUE._serialized_end=313 +# @@protoc_insertion_point(module_scope) diff --git a/resources/fanart.jpg b/resources/fanart.jpg new file mode 100644 index 0000000..d05ecb0 Binary files /dev/null and b/resources/fanart.jpg differ diff --git a/resources/icon.png b/resources/icon.png new file mode 100644 index 0000000..a64b15b Binary files /dev/null and b/resources/icon.png differ diff --git a/resources/icon_clear_cache.png b/resources/icon_clear_cache.png new file mode 100644 index 0000000..f1f1ae6 Binary files /dev/null and b/resources/icon_clear_cache.png differ diff --git a/resources/icon_music_albums.png b/resources/icon_music_albums.png new file mode 100644 index 0000000..ea4d180 Binary files /dev/null and b/resources/icon_music_albums.png differ diff --git a/resources/icon_music_artists.png b/resources/icon_music_artists.png new file mode 100644 index 0000000..c9db24d Binary files /dev/null and b/resources/icon_music_artists.png differ diff --git a/resources/icon_music_explore.png b/resources/icon_music_explore.png new file mode 100644 index 0000000..a50170e Binary files /dev/null and b/resources/icon_music_explore.png differ diff --git a/resources/icon_music_library.png b/resources/icon_music_library.png new file mode 100644 index 0000000..2ed2bc8 Binary files /dev/null and b/resources/icon_music_library.png differ diff --git a/resources/icon_music_playlists.png b/resources/icon_music_playlists.png new file mode 100644 index 0000000..bb85a04 Binary files /dev/null and b/resources/icon_music_playlists.png differ diff --git a/resources/icon_music_search.png b/resources/icon_music_search.png new file mode 100644 index 0000000..7a74fb7 Binary files /dev/null and b/resources/icon_music_search.png differ diff --git a/resources/icon_music_songs.png b/resources/icon_music_songs.png new file mode 100644 index 0000000..3c2a157 Binary files /dev/null and b/resources/icon_music_songs.png differ diff --git a/resources/icon_music_top_artists.png b/resources/icon_music_top_artists.png new file mode 100644 index 0000000..617d961 Binary files /dev/null and b/resources/icon_music_top_artists.png differ diff --git a/resources/icon_music_top_tracks.png b/resources/icon_music_top_tracks.png new file mode 100644 index 0000000..010b021 Binary files /dev/null and b/resources/icon_music_top_tracks.png differ diff --git a/resources/language/resource.language.de_de/strings.po b/resources/language/resource.language.de_de/strings.po new file mode 100644 index 0000000..14381b2 --- /dev/null +++ b/resources/language/resource.language.de_de/strings.po @@ -0,0 +1,179 @@ +# Kodi Media Center language file +# Addon Name: Spotify +# Addon id: plugin.audio.spotify +# Addon Provider: marcelveldt +msgid "" +msgstr "" +"Project-Id-Version: Kodi-Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: 2015-11-04 18:30+0100\n" +"PO-Revision-Date: 2015-11-04 18:30+0100\n" +"Last-Translator: tobhor\n" +"Language-Team: German\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: de\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgctxt "#11001" +msgid "Username" +msgstr "Nutzername" + +msgctxt "#11002" +msgid "Password" +msgstr "Passwort" + +msgctxt "#11005" +msgid "New releases" +msgstr "Neuerscheinungen" + +msgctxt "#11007" +msgid "Save to My Music" +msgstr "Zu meiner Musik hinzufügen" + +msgctxt "#11008" +msgid "Remove from My Music" +msgstr "Aus meiner Musik entfernen" + +msgctxt "#11009" +msgid "Follow" +msgstr "Folgen" + +msgctxt "#11010" +msgid "Unfollow" +msgstr "Nicht mehr folgen" + +msgctxt "#11011" +msgid "Artist top tracks" +msgstr "Beliebteste Lieder des Künstlers" + +msgctxt "#11012" +msgid "Related artists" +msgstr "Ähnliche Künstler" + +msgctxt "#11013" +msgid "My Music" +msgstr "Meine Musik" + +msgctxt "#11014" +msgid "Explore" +msgstr "Entdecken" + +msgctxt "#11015" +msgid "Featured playlists" +msgstr "Empfohlene Wiedergabelisten" + +msgctxt "#11016" +msgid "New releases" +msgstr "Neuerscheinungen" + +msgctxt "#11017" +msgid "Remove from playlist" +msgstr "Aus Playlist entfernen" + +msgctxt "#11018" +msgid "All albums for artist" +msgstr "Alle Alben des Künstlers" + +msgctxt "#11020" +msgid "Default view for categories" +msgstr "" + +msgctxt "#11023" +msgid "Most played artists" +msgstr "Meistgespielte Künstler" + +msgctxt "#11024" +msgid "Most played tracks" +msgstr "Meistgespielte Lieder" + +msgctxt "#11025" +msgid "Follow artist" +msgstr "Künstler folgen" + +msgctxt "#11026" +msgid "Unfollow artist" +msgstr "Künstler nicht mehr folgen" + +msgctxt "#11027" +msgid "Refresh listing" +msgstr "Eintrag aktualisieren" + +msgctxt "#11028" +msgid "Login details" +msgstr "Anmeldedetails" + +msgctxt "#11030" +msgid "Append artist name to song title" +msgstr "Künstlername an Songtitel anhängen" + +msgctxt "#11031" +msgid "Default view for playlists" +msgstr "Standardansicht für Wiedergabeliste" + +msgctxt "#11032" +msgid "Default view for artist list" +msgstr "Standardansicht für Künstlerliste" + +msgctxt "#11033" +msgid "Default view for album list" +msgstr "Standardansicht für Albenliste" + +msgctxt "#11034" +msgid "Default view for song list" +msgstr "Standardansicht für Songliste" + +msgctxt "#11047" +msgid "Current user" +msgstr "Aktueller Benutzer" + +msgctxt "#11050" +msgid "" +"Spotify login failed. Either no credentials have been set or the Spotify username or password is incorrect.\n" +"Please check your username and password in the addon settings." +msgstr "Keine Anmeldedaten vorhanden oder Anmeldung fehlgeschlagen. \n Bitte gebe deine Anmeldedaten im folgenden Einstellungsdialog ein." + +msgctxt "#11054" +msgid "Audio" +msgstr "Audio" + +msgctxt "#11055" +msgid "Views" +msgstr "Ansichten" + +msgctxt "#11069" +msgid "My recently played playlist" +msgstr "My recently played playlist" + +msgctxt "#11070" +msgid "Gap between tracks when playing a playlist (secs)" +msgstr "Gap between tracks when playing a playlist (secs)" + +msgctxt "#11071" +msgid "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." +msgstr "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." + +msgctxt "#11072" +msgid "Clear the plugin cache" +msgstr "Clear the plugin cache" + +msgctxt "#11073" +msgid "Followed artists" +msgstr "Followed artists" + +msgctxt "#11074" +msgid "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." +msgstr "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." + +msgctxt "#11075" +msgid "Use Spotify normalization when playing tracks" +msgstr "" diff --git a/resources/language/resource.language.en_gb/strings.po b/resources/language/resource.language.en_gb/strings.po new file mode 100644 index 0000000..c914125 --- /dev/null +++ b/resources/language/resource.language.en_gb/strings.po @@ -0,0 +1,175 @@ +# Kodi Media Center language file +# Addon Name: Spotify +# Addon id: plugin.audio.spotify +# Addon Provider: marcelveldt +msgid "" +msgstr "" +"Project-Id-Version: Kodi-Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: 2015-11-04 18:30+0100\n" +"PO-Revision-Date: 2015-11-04 18:30+0100\n" +"Last-Translator: logi85\n" +"Language-Team: English\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: en\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgctxt "#11001" +msgid "Username" +msgstr "" + +msgctxt "#11002" +msgid "Password" +msgstr "" + +msgctxt "#11005" +msgid "New releases" +msgstr "" + +msgctxt "#11007" +msgid "Save to My Music" +msgstr "" + +msgctxt "#11008" +msgid "Remove from My Music" +msgstr "" + +msgctxt "#11009" +msgid "Follow" +msgstr "" + +msgctxt "#11010" +msgid "Unfollow" +msgstr "" + +msgctxt "#11011" +msgid "Artist top tracks" +msgstr "" + +msgctxt "#11012" +msgid "Related artists" +msgstr "" + +msgctxt "#11013" +msgid "My Music" +msgstr "" + +msgctxt "#11014" +msgid "Explore" +msgstr "" + +msgctxt "#11015" +msgid "Featured playlists" +msgstr "" + +msgctxt "#11016" +msgid "New releases" +msgstr "" + +msgctxt "#11017" +msgid "Remove from playlist" +msgstr "" + +msgctxt "#11018" +msgid "All albums for artist" +msgstr "" + +msgctxt "#11020" +msgid "Default view for categories" +msgstr "" + +msgctxt "#11023" +msgid "Most played artists" +msgstr "" + +msgctxt "#11024" +msgid "Most played tracks" +msgstr "" + +msgctxt "#11025" +msgid "Follow artist" +msgstr "" + +msgctxt "#11026" +msgid "Unfollow artist" +msgstr "" + +msgctxt "#11027" +msgid "Refresh listing" +msgstr "" + +msgctxt "#11028" +msgid "Login details" +msgstr "" + +msgctxt "#11030" +msgid "Append artist name to song title" +msgstr "" + +msgctxt "#11031" +msgid "Default view for playlists" +msgstr "" + +msgctxt "#11032" +msgid "Default view for artist list" +msgstr "" + +msgctxt "#11033" +msgid "Default view for album list" +msgstr "" + +msgctxt "#11034" +msgid "Default view for song list" +msgstr "" + +msgctxt "#11047" +msgid "Current user" +msgstr "" + +msgctxt "#11050" +msgid "" +"Spotify login failed. Either no credentials have been set or the Spotify username or password is incorrect.\n" +"Please check your username and password in the addon settings." +msgstr "" + +msgctxt "#11054" +msgid "Audio" +msgstr "" + +msgctxt "#11055" +msgid "Views" +msgstr "" + +msgctxt "#11069" +msgid "My recently played playlist" +msgstr "" + +msgctxt "#11070" +msgid "Gap between tracks when playing a playlist (secs)" +msgstr "" + +msgctxt "#11071" +msgid "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." +msgstr "" + +msgctxt "#11072" +msgid "Clear the plugin cache" +msgstr "" + +msgctxt "#11073" +msgid "Followed artists" +msgstr "" + +msgctxt "#11074" +msgid "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." +msgstr "" + +msgctxt "#11075" +msgid "Use Spotify normalization when playing tracks" +msgstr "" diff --git a/resources/language/resource.language.es_ar/strings.po b/resources/language/resource.language.es_ar/strings.po new file mode 100644 index 0000000..cdd7253 --- /dev/null +++ b/resources/language/resource.language.es_ar/strings.po @@ -0,0 +1,175 @@ +# Kodi Media Center language file +# Addon Name: Spotify +# Addon id: plugin.audio.spotify +# Addon Provider: marcelveldt +msgid "" +msgstr "" +"Project-Id-Version: Kodi-Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: 2015-11-04 18:30+0100\n" +"PO-Revision-Date: 2015-11-04 18:30+0100\n" +"Last-Translator: trihy\n" +"Language-Team: English\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es_ar\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgctxt "#11001" +msgid "Username" +msgstr "Nombre de Usuario" + +msgctxt "#11002" +msgid "Password" +msgstr "Contraseña" + +msgctxt "#11005" +msgid "New releases" +msgstr "Nuevos Lanzamientos" + +msgctxt "#11007" +msgid "Save to My Music" +msgstr "Guardar en Mi Música" + +msgctxt "#11008" +msgid "Remove from My Music" +msgstr "Remover de Mi Música" + +msgctxt "#11009" +msgid "Follow" +msgstr "Seguir" + +msgctxt "#11010" +msgid "Unfollow" +msgstr "Dejar de Seguir" + +msgctxt "#11011" +msgid "Artist top tracks" +msgstr "Canciones Top del Artista" + +msgctxt "#11012" +msgid "Related artists" +msgstr "Artistas Relacionados" + +msgctxt "#11013" +msgid "My Music" +msgstr "Mi Música" + +msgctxt "#11014" +msgid "Explore" +msgstr "Explorar" + +msgctxt "#11015" +msgid "Featured playlists" +msgstr "Playlists Destacadas" + +msgctxt "#11016" +msgid "New releases" +msgstr "Nuevos Lanzamientos" + +msgctxt "#11017" +msgid "Remove from playlist" +msgstr "Remover del Playlist" + +msgctxt "#11018" +msgid "All albums for artist" +msgstr "Todos los Albumes del Artista" + +msgctxt "#11020" +msgid "Default view for categories" +msgstr "Vista por Defecto para Categorías" + +msgctxt "#11023" +msgid "Most played artists" +msgstr "Artistas más Reproducidos " + +msgctxt "#11024" +msgid "Most played tracks" +msgstr "Canciones más Reproducidas" + +msgctxt "#11025" +msgid "Follow artist" +msgstr "Seguir Artista" + +msgctxt "#11026" +msgid "Unfollow artist" +msgstr "Dejar de Seguir Artista" + +msgctxt "#11027" +msgid "Refresh listing" +msgstr "Refrescar Listado" + +msgctxt "#11028" +msgid "Login details" +msgstr "Detalles de Login" + +msgctxt "#11030" +msgid "Append artist name to song title" +msgstr "Adjuntar Nombre del Artista a Título de Canción" + +msgctxt "#11031" +msgid "Default view for playlists" +msgstr "Vista por Defecto para Playlists" + +msgctxt "#11032" +msgid "Default view for artist list" +msgstr "Vista por Defecto para Lista de Artista" + +msgctxt "#11033" +msgid "Default view for album list" +msgstr "Vista por Defecto para Lista de Album" + +msgctxt "#11034" +msgid "Default view for song list" +msgstr "Vista por Defecto para Lista de Canción" + +msgctxt "#11047" +msgid "Current user" +msgstr "Usuario Actual" + +msgctxt "#11050" +msgid "" +"Spotify login failed. Either no credentials have been set or the Spotify username or password is incorrect.\n" +"Please check your username and password in the addon settings." +msgstr "" + +msgctxt "#11054" +msgid "Audio" +msgstr "Audio" + +msgctxt "#11055" +msgid "Views" +msgstr "Vistas" + +msgctxt "#11069" +msgid "My recently played playlist" +msgstr "Mi Playlist reproducida Recientemente" + +msgctxt "#11070" +msgid "Gap between tracks when playing a playlist (secs)" +msgstr "Espacio entre canciones cuando se reproduce una playlist (Segundos)" + +msgctxt "#11071" +msgid "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." +msgstr "" + +msgctxt "#11072" +msgid "Clear the plugin cache" +msgstr "Limpiar cache del plugin" + +msgctxt "#11073" +msgid "Followed artists" +msgstr "Artistas Seguidos" + +msgctxt "#11074" +msgid "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." +msgstr "Cache de base de datos del plugin exitosamente limpiada" + +msgctxt "#11075" +msgid "Use Spotify normalization when playing tracks" +msgstr "Usar Normalizacion de Spotify cuando reproduzca canciones" diff --git a/resources/language/resource.language.es_es/strings.po b/resources/language/resource.language.es_es/strings.po new file mode 100644 index 0000000..61ca289 --- /dev/null +++ b/resources/language/resource.language.es_es/strings.po @@ -0,0 +1,175 @@ +# Kodi Media Center language file +# Addon Name: Spotify +# Addon id: plugin.audio.spotify +# Addon Provider: marcelveldt +msgid "" +msgstr "" +"Project-Id-Version: Kodi-Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: 2015-11-04 18:30+0100\n" +"PO-Revision-Date: 2015-11-04 18:30+0100\n" +"Last-Translator: trihy\n" +"Language-Team: English\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es_es\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgctxt "#11001" +msgid "Username" +msgstr "Nombre de Usuario" + +msgctxt "#11002" +msgid "Password" +msgstr "Contraseña" + +msgctxt "#11005" +msgid "New releases" +msgstr "Nuevos Lanzamientos" + +msgctxt "#11007" +msgid "Save to My Music" +msgstr "Guardar en Mi Música" + +msgctxt "#11008" +msgid "Remove from My Music" +msgstr "Remover de Mi Música" + +msgctxt "#11009" +msgid "Follow" +msgstr "Seguir" + +msgctxt "#11010" +msgid "Unfollow" +msgstr "Dejar de Seguir" + +msgctxt "#11011" +msgid "Artist top tracks" +msgstr "Canciones Top del Artista" + +msgctxt "#11012" +msgid "Related artists" +msgstr "Artistas Relacionados" + +msgctxt "#11013" +msgid "My Music" +msgstr "Mi Música" + +msgctxt "#11014" +msgid "Explore" +msgstr "Explorar" + +msgctxt "#11015" +msgid "Featured playlists" +msgstr "Playlists Destacadas" + +msgctxt "#11016" +msgid "New releases" +msgstr "Nuevos Lanzamientos" + +msgctxt "#11017" +msgid "Remove from playlist" +msgstr "Remover del Playlist" + +msgctxt "#11018" +msgid "All albums for artist" +msgstr "Todos los Albumes del Artista" + +msgctxt "#11020" +msgid "Default view for categories" +msgstr "Vista por Defecto para Categorías" + +msgctxt "#11023" +msgid "Most played artists" +msgstr "Artistas más Reproducidos " + +msgctxt "#11024" +msgid "Most played tracks" +msgstr "Canciones más Reproducidas" + +msgctxt "#11025" +msgid "Follow artist" +msgstr "Seguir Artista" + +msgctxt "#11026" +msgid "Unfollow artist" +msgstr "Dejar de Seguir Artista" + +msgctxt "#11027" +msgid "Refresh listing" +msgstr "Refrescar Listado" + +msgctxt "#11028" +msgid "Login details" +msgstr "Detalles de Login" + +msgctxt "#11030" +msgid "Append artist name to song title" +msgstr "Adjuntar Nombre del Artista a Título de Canción" + +msgctxt "#11031" +msgid "Default view for playlists" +msgstr "Vista por Defecto para Playlists" + +msgctxt "#11032" +msgid "Default view for artist list" +msgstr "Vista por Defecto para Lista de Artista" + +msgctxt "#11033" +msgid "Default view for album list" +msgstr "Vista por Defecto para Lista de Album" + +msgctxt "#11034" +msgid "Default view for song list" +msgstr "Vista por Defecto para Lista de Canción" + +msgctxt "#11047" +msgid "Current user" +msgstr "Usuario Actual" + +msgctxt "#11050" +msgid "" +"Spotify login failed. Either no credentials have been set or the Spotify username or password is incorrect.\n" +"Please check your username and password in the addon settings." +msgstr "" + +msgctxt "#11054" +msgid "Audio" +msgstr "Audio" + +msgctxt "#11055" +msgid "Views" +msgstr "Vistas" + +msgctxt "#11069" +msgid "My recently played playlist" +msgstr "Mi Playlist reproducida Recientemente" + +msgctxt "#11070" +msgid "Gap between tracks when playing a playlist (secs)" +msgstr "Espacio entre canciones cuando se reproduce una playlist (Segundos)" + +msgctxt "#11071" +msgid "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." +msgstr "" + +msgctxt "#11072" +msgid "Clear the plugin cache" +msgstr "Limpiar cache del plugin" + +msgctxt "#11073" +msgid "Followed artists" +msgstr "Artistas Seguidos" + +msgctxt "#11074" +msgid "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." +msgstr "Cache de base de datos del plugin exitosamente limpiada" + +msgctxt "#11075" +msgid "Use Spotify normalization when playing tracks" +msgstr "Usar Normalizacion de Spotify cuando reproduzca canciones" diff --git a/resources/language/resource.language.es_mx/strings.po b/resources/language/resource.language.es_mx/strings.po new file mode 100644 index 0000000..6c85d38 --- /dev/null +++ b/resources/language/resource.language.es_mx/strings.po @@ -0,0 +1,175 @@ +# Kodi Media Center language file +# Addon Name: Spotify +# Addon id: plugin.audio.spotify +# Addon Provider: marcelveldt +msgid "" +msgstr "" +"Project-Id-Version: Kodi-Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: 2015-11-04 18:30+0100\n" +"PO-Revision-Date: 2015-11-04 18:30+0100\n" +"Last-Translator: trihy\n" +"Language-Team: English\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: es_mx\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgctxt "#11001" +msgid "Username" +msgstr "Nombre de Usuario" + +msgctxt "#11002" +msgid "Password" +msgstr "Contraseña" + +msgctxt "#11005" +msgid "New releases" +msgstr "Nuevos Lanzamientos" + +msgctxt "#11007" +msgid "Save to My Music" +msgstr "Guardar en Mi Música" + +msgctxt "#11008" +msgid "Remove from My Music" +msgstr "Remover de Mi Música" + +msgctxt "#11009" +msgid "Follow" +msgstr "Seguir" + +msgctxt "#11010" +msgid "Unfollow" +msgstr "Dejar de Seguir" + +msgctxt "#11011" +msgid "Artist top tracks" +msgstr "Canciones Top del Artista" + +msgctxt "#11012" +msgid "Related artists" +msgstr "Artistas Relacionados" + +msgctxt "#11013" +msgid "My Music" +msgstr "Mi Música" + +msgctxt "#11014" +msgid "Explore" +msgstr "Explorar" + +msgctxt "#11015" +msgid "Featured playlists" +msgstr "Playlists Destacadas" + +msgctxt "#11016" +msgid "New releases" +msgstr "Nuevos Lanzamientos" + +msgctxt "#11017" +msgid "Remove from playlist" +msgstr "Remover del Playlist" + +msgctxt "#11018" +msgid "All albums for artist" +msgstr "Todos los Albumes del Artista" + +msgctxt "#11020" +msgid "Default view for categories" +msgstr "Vista por Defecto para Categorías" + +msgctxt "#11023" +msgid "Most played artists" +msgstr "Artistas más Reproducidos " + +msgctxt "#11024" +msgid "Most played tracks" +msgstr "Canciones más Reproducidas" + +msgctxt "#11025" +msgid "Follow artist" +msgstr "Seguir Artista" + +msgctxt "#11026" +msgid "Unfollow artist" +msgstr "Dejar de Seguir Artista" + +msgctxt "#11027" +msgid "Refresh listing" +msgstr "Refrescar Listado" + +msgctxt "#11028" +msgid "Login details" +msgstr "Detalles de Login" + +msgctxt "#11030" +msgid "Append artist name to song title" +msgstr "Adjuntar Nombre del Artista a Título de Canción" + +msgctxt "#11031" +msgid "Default view for playlists" +msgstr "Vista por Defecto para Playlists" + +msgctxt "#11032" +msgid "Default view for artist list" +msgstr "Vista por Defecto para Lista de Artista" + +msgctxt "#11033" +msgid "Default view for album list" +msgstr "Vista por Defecto para Lista de Album" + +msgctxt "#11034" +msgid "Default view for song list" +msgstr "Vista por Defecto para Lista de Canción" + +msgctxt "#11047" +msgid "Current user" +msgstr "Usuario Actual" + +msgctxt "#11050" +msgid "" +"Spotify login failed. Either no credentials have been set or the Spotify username or password is incorrect.\n" +"Please check your username and password in the addon settings." +msgstr "" + +msgctxt "#11054" +msgid "Audio" +msgstr "Audio" + +msgctxt "#11055" +msgid "Views" +msgstr "Vistas" + +msgctxt "#11069" +msgid "My recently played playlist" +msgstr "Mi Playlist reproducida Recientemente" + +msgctxt "#11070" +msgid "Gap between tracks when playing a playlist (secs)" +msgstr "Espacio entre canciones cuando se reproduce una playlist (Segundos)" + +msgctxt "#11071" +msgid "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." +msgstr "" + +msgctxt "#11072" +msgid "Clear the plugin cache" +msgstr "Limpiar cache del plugin" + +msgctxt "#11073" +msgid "Followed artists" +msgstr "Artistas Seguidos" + +msgctxt "#11074" +msgid "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." +msgstr "Cache de base de datos del plugin exitosamente limpiada" + +msgctxt "#11075" +msgid "Use Spotify normalization when playing tracks" +msgstr "Usar Normalizacion de Spotify cuando reproduzca canciones" diff --git a/resources/language/resource.language.fr_fr/strings.po b/resources/language/resource.language.fr_fr/strings.po new file mode 100644 index 0000000..3938f3d --- /dev/null +++ b/resources/language/resource.language.fr_fr/strings.po @@ -0,0 +1,179 @@ +# Kodi Media Center language file +# Addon Name: Spotify +# Addon id: plugin.audio.spotify +# Addon Provider: marcelveldt +msgid "" +msgstr "" +"Project-Id-Version: Kodi-Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: 2015-11-04 18:30+0100\n" +"PO-Revision-Date: 2015-11-04 18:30+0100\n" +"Last-Translator: xsellier\n" +"Language-Team: French\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: en\n" +"Plural-Forms: nplurals=2; plural=(n != 1)\n" + +msgctxt "#11001" +msgid "Username" +msgstr "Nom d'utilisateur" + +msgctxt "#11002" +msgid "Password" +msgstr "Mot de passe" + +msgctxt "#11005" +msgid "New releases" +msgstr "Nouveautés" + +msgctxt "#11007" +msgid "Save to My Music" +msgstr "Ajouter a Ma Musique" + +msgctxt "#11008" +msgid "Remove from My Music" +msgstr "Enlever de Ma Musique" + +msgctxt "#11009" +msgid "Follow" +msgstr "Suivre" + +msgctxt "#11010" +msgid "Unfollow" +msgstr "Arrêter de suivre" + +msgctxt "#11011" +msgid "Artist top tracks" +msgstr "Les meilleures chansons de l'artiste" + +msgctxt "#11012" +msgid "Related artists" +msgstr "Artistes similaires" + +msgctxt "#11013" +msgid "My Music" +msgstr "Ma Musique" + +msgctxt "#11014" +msgid "Explore" +msgstr "Explorer" + +msgctxt "#11015" +msgid "Featured playlists" +msgstr "Listes de lectures mises en avant" + +msgctxt "#11016" +msgid "New releases" +msgstr "Nouveautés" + +msgctxt "#11017" +msgid "Remove from playlist" +msgstr "Enlever de la liste de lecture" + +msgctxt "#11018" +msgid "All albums for artist" +msgstr "Tous les albums de cet artiste" + +msgctxt "#11020" +msgid "Default view for categories" +msgstr "" + +msgctxt "#11023" +msgid "Most played artists" +msgstr "Les artistes les plus écoutés" + +msgctxt "#11024" +msgid "Most played tracks" +msgstr "Les musiques les plus jouées" + +msgctxt "#11025" +msgid "Follow artist" +msgstr "Suivre l'artiste" + +msgctxt "#11026" +msgid "Unfollow artist" +msgstr "Arrêter de suivre l'artiste" + +msgctxt "#11027" +msgid "Refresh listing" +msgstr "Rafraîchir la liste" + +msgctxt "#11028" +msgid "Login details" +msgstr "Détails de l'authentification" + +msgctxt "#11030" +msgid "Append artist name to song title" +msgstr "Mettre l nom de l'artiste à la suite du titre de la chanson" + +msgctxt "#11031" +msgid "Default view for playlists" +msgstr "Liste de lecture comme vue par défaut" + +msgctxt "#11032" +msgid "Default view for artist list" +msgstr "Liste d'artistes comme vue par défaut" + +msgctxt "#11033" +msgid "Default view for album list" +msgstr "Liste d'albums comme vue par défaut" + +msgctxt "#11034" +msgid "Default view for song list" +msgstr "Liste de chansons comme vue par défaut" + +msgctxt "#11047" +msgid "Current user" +msgstr "" + +msgctxt "#11050" +msgid "" +"Spotify login failed. Either no credentials have been set or the Spotify username or password is incorrect.\n" +"Please check your username and password in the addon settings." +msgstr "" + +msgctxt "#11054" +msgid "Audio" +msgstr "" + +msgctxt "#11055" +msgid "Views" +msgstr "" + +msgctxt "#11069" +msgid "My recently played playlist" +msgstr "My recently played playlist" + +msgctxt "#11070" +msgid "Gap between tracks when playing a playlist (secs)" +msgstr "Gap between tracks when playing a playlist (secs)" + +msgctxt "#11071" +msgid "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." +msgstr "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." + +msgctxt "#11072" +msgid "Clear the plugin cache" +msgstr "Clear the plugin cache" + +msgctxt "#11073" +msgid "Followed artists" +msgstr "Followed artists" + +msgctxt "#11074" +msgid "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." +msgstr "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." + +msgctxt "#11075" +msgid "Use Spotify normalization when playing tracks" +msgstr "" diff --git a/resources/language/resource.language.he_he/strings.po b/resources/language/resource.language.he_he/strings.po new file mode 100644 index 0000000..07bb55c --- /dev/null +++ b/resources/language/resource.language.he_he/strings.po @@ -0,0 +1,182 @@ +# Kodi Media Center language file +# Addon Name: Spotify +# Addon id: plugin.audio.spotify +# Addon Provider: marcelveldt +msgid "" +msgstr "" +"Project-Id-Version: Kodi-Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: 2015-11-04 18:30+0100\n" +"PO-Revision-Date: 2017-09-28 14:10+0300\n" +"Last-Translator: A. Dambledore\n" +"Language-Team: Eng2Heb\n" +"Language: he_IL\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.0.4\n" + +msgctxt "#11001" +msgid "Username" +msgstr "שם משתמש" + +msgctxt "#11002" +msgid "Password" +msgstr "ססמה" + +msgctxt "#11005" +msgid "New releases" +msgstr "שיחרורים חדשים" + +msgctxt "#11007" +msgid "Save to My Music" +msgstr "לשמור אל המוזיקה שלי" + +msgctxt "#11008" +msgid "Remove from My Music" +msgstr "הסר מהמוזיקה שלי" + +msgctxt "#11009" +msgid "Follow" +msgstr "עקוב" + +msgctxt "#11010" +msgid "Unfollow" +msgstr "ביטול מעקב" + +msgctxt "#11011" +msgid "Artist top tracks" +msgstr "השירים הכי טובים של האמן" + +msgctxt "#11012" +msgid "Related artists" +msgstr "אמנים קשורים" + +msgctxt "#11013" +msgid "My Music" +msgstr "המוזיקה שלי" + +msgctxt "#11014" +msgid "Explore" +msgstr "חקור" + +msgctxt "#11015" +msgid "Featured playlists" +msgstr "רשימות ניגון מומלצות" + +msgctxt "#11016" +msgid "New releases" +msgstr "שיחרורים חדשים" + +msgctxt "#11017" +msgid "Remove from playlist" +msgstr "הסר מרשימת ניגון" + +msgctxt "#11018" +msgid "All albums for artist" +msgstr "כל האלבומים עבור אמן" + +msgctxt "#11020" +msgid "Default view for categories" +msgstr "" + +msgctxt "#11023" +msgid "Most played artists" +msgstr "האמנים הכי מושמעים" + +msgctxt "#11024" +msgid "Most played tracks" +msgstr "השירים הכי מושמעים" + +msgctxt "#11025" +msgid "Follow artist" +msgstr "עקוב אחר האמן" + +msgctxt "#11026" +msgid "Unfollow artist" +msgstr "בטל עקיבה אחר האמן" + +msgctxt "#11027" +msgid "Refresh listing" +msgstr "רענן את הרישום" + +msgctxt "#11028" +msgid "Login details" +msgstr "פרטי התחברות" + +msgctxt "#11030" +msgid "Append artist name to song title" +msgstr "הוסף שם אמן לכותרת השיר" + +msgctxt "#11031" +msgid "Default view for playlists" +msgstr "תצוגת ברירת המחדל עבור רשימות ניגון" + +msgctxt "#11032" +msgid "Default view for artist list" +msgstr "תצוגת ברירת המחדל עבור רשימת האמנים" + +msgctxt "#11033" +msgid "Default view for album list" +msgstr "תצוגת ברירת המחדל עבור רשימת אלבומים" + +msgctxt "#11034" +msgid "Default view for song list" +msgstr "תצוגת ברירת המחדל עבור רשימת השירים" + +msgctxt "#11047" +msgid "Current user" +msgstr "משתמש נוכחי" + +msgctxt "#11050" +msgid "" +"Spotify login failed. Either no credentials have been set or the Spotify username or password is incorrect.\n" +"Please check your username and password in the addon settings." +msgstr "" +"לא הוגדרו אישורי כניסה או שהחיבור נכשל.\n" +"הזן את הפרטים שלך בתיבת הדו-שיח הגדרות." + +msgctxt "#11054" +msgid "Audio" +msgstr "אודיו" + +msgctxt "#11055" +msgid "Views" +msgstr "צפיות" + +msgctxt "#11069" +msgid "My recently played playlist" +msgstr "My recently played playlist" + +msgctxt "#11070" +msgid "Gap between tracks when playing a playlist (secs)" +msgstr "Gap between tracks when playing a playlist (secs)" + +msgctxt "#11071" +msgid "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." +msgstr "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." + +msgctxt "#11072" +msgid "Clear the plugin cache" +msgstr "Clear the plugin cache" + +msgctxt "#11073" +msgid "Followed artists" +msgstr "Followed artists" + +msgctxt "#11074" +msgid "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." +msgstr "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." + +msgctxt "#11075" +msgid "Use Spotify normalization when playing tracks" +msgstr "" diff --git a/resources/language/resource.language.nl_nl/strings.mo b/resources/language/resource.language.nl_nl/strings.mo new file mode 100644 index 0000000..8c7d83b Binary files /dev/null and b/resources/language/resource.language.nl_nl/strings.mo differ diff --git a/resources/language/resource.language.nl_nl/strings.po b/resources/language/resource.language.nl_nl/strings.po new file mode 100644 index 0000000..fc75d5e --- /dev/null +++ b/resources/language/resource.language.nl_nl/strings.po @@ -0,0 +1,179 @@ +# Kodi Media Center language file +# Addon Name: Spotify +# Addon id: plugin.audio.spotify +# Addon Provider: marcelveldt +msgid "" +msgstr "" +"Project-Id-Version: Kodi-Addons\n" +"Report-Msgid-Bugs-To: alanwww1@xbmc.org\n" +"POT-Creation-Date: 2015-11-04 18:30+0100\n" +"PO-Revision-Date: 2019-07-18 15:34+0200\n" +"Last-Translator: logi85\n" +"Language-Team: Dutch\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Language: nl\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" +"X-Generator: Poedit 2.2.3\n" + +msgctxt "#11001" +msgid "Username" +msgstr "Gebruikersnaam" + +msgctxt "#11002" +msgid "Password" +msgstr "Wachtwoord" + +msgctxt "#11005" +msgid "New releases" +msgstr "Nieuwe releases" + +msgctxt "#11007" +msgid "Save to My Music" +msgstr "Opslaan naar Mijn Muziek" + +msgctxt "#11008" +msgid "Remove from My Music" +msgstr "Verwijder uit Mijn Muziek" + +msgctxt "#11009" +msgid "Follow" +msgstr "Volgen" + +msgctxt "#11010" +msgid "Unfollow" +msgstr "Niet meer volgen" + +msgctxt "#11011" +msgid "Artist top tracks" +msgstr "Artiest top-nummers" + +msgctxt "#11012" +msgid "Related artists" +msgstr "Gerelateerde artiesten" + +msgctxt "#11013" +msgid "My Music" +msgstr "Mijn Muziek" + +msgctxt "#11014" +msgid "Explore" +msgstr "Verkennen" + +msgctxt "#11015" +msgid "Featured playlists" +msgstr "Aanbevolen afspeellijsten" + +msgctxt "#11016" +msgid "New releases" +msgstr "Nieuwe releases" + +msgctxt "#11017" +msgid "Remove from playlist" +msgstr "Verwijder uit afspeellijst" + +msgctxt "#11018" +msgid "All albums for artist" +msgstr "Alle albums van artiest" + +msgctxt "#11020" +msgid "Default view for categories" +msgstr "" + +msgctxt "#11023" +msgid "Most played artists" +msgstr "Meest afgespeelde artiesten" + +msgctxt "#11024" +msgid "Most played tracks" +msgstr "Meest afgespeelde liedjes" + +msgctxt "#11025" +msgid "Follow artist" +msgstr "Volg artiest" + +msgctxt "#11026" +msgid "Unfollow artist" +msgstr "Artiest niet volgen" + +msgctxt "#11027" +msgid "Refresh listing" +msgstr "Lijst vernieuwen" + +msgctxt "#11036" +msgid "Enable this device as Spotify Connect target (experimental)" +msgstr "Schakel dit apparaat in als Spotify Connect doel (experimenteel)" + +msgctxt "#11031" +msgid "Default view for playlists" +msgstr "" + +msgctxt "#11032" +msgid "Default view for artist list" +msgstr "" + +msgctxt "#11033" +msgid "Default view for album list" +msgstr "" + +msgctxt "#11034" +msgid "Default view for song list" +msgstr "" + +msgctxt "#11047" +msgid "Current user" +msgstr "Huidige gebruiker" + +msgctxt "#11050" +msgid "" +"Spotify login failed. Either no credentials have been set or the Spotify username or password is incorrect.\n" +"Please check your username and password in the addon settings." +msgstr "" +"Er zijn geen inloggegevens gevonden of het inloggen faalt.\n" +"Voer alsjeblieft je inloggegevens in in de plugin instellingen." + +msgctxt "#11054" +msgid "Audio" +msgstr "Audio" + +#, fuzzy +msgctxt "#11055" +msgid "Views" +msgstr "Weergave" + +msgctxt "#11069" +msgid "My recently played playlist" +msgstr "My recently played playlist" + +msgctxt "#11070" +msgid "Gap between tracks when playing a playlist (secs)" +msgstr "Gap between tracks when playing a playlist (secs)" + +msgctxt "#11071" +msgid "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." +msgstr "" +"To give better audio streaming from Spotify, a video http rule was just added to 'userdata/playercorefactory.xml'.\n" +"Please restart Kodi for this to take affect." + +msgctxt "#11072" +msgid "Clear the plugin cache" +msgstr "Clear the plugin cache" + +msgctxt "#11073" +msgid "Followed artists" +msgstr "Followed artists" + +msgctxt "#11074" +msgid "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." +msgstr "" +"Successfully cleared the plugin cache database.\n" +"Please restart Kodi for this to take affect." + +msgctxt "#11075" +msgid "Use Spotify normalization when playing tracks" +msgstr "" diff --git a/resources/lib/__init__.py b/resources/lib/__init__.py new file mode 100644 index 0000000..f6375d1 --- /dev/null +++ b/resources/lib/__init__.py @@ -0,0 +1,50 @@ +import os +import sys + +sys.path.insert(1, os.path.join(os.path.dirname(__file__))) + +# IMPORTANT: The 'cherrypy' module cannot be imported as a submodule from 'httpproxy.py'. +# I.e, 'from deps import cherrypy' will not work. Not sure why. So we do the following +# path hack to put 'cherrypy' on the module search path: +sys.path.insert(1, os.path.join(os.path.dirname(__file__), "deps")) + + +# import pkgutil + +# def list_submodules(list_name, package_name): +# for loader, module_name, is_pkg in pkgutil.walk_packages( +# package_name.__path__, package_name.__name__ + "." +# ): +# list_name.append(module_name) +# print(f"module_name 1 = {module_name}, is_pkg = {is_pkg}.") +# try: +# module_name = __import__(module_name, fromlist="dummylist") +# except Exception as ex: +# print(ex) +# module_name = None +# print(f"module_name 2 = {module_name}.") +# if is_pkg: +# list_submodules(list_name, module_name) +# +# +# if len(sys.argv) != 2: +# print("Usage: {} [PACKAGE-NAME]".format(os.path.basename(__file__))) +# sys.exit(1) +# else: +# package_name = sys.argv[1] +# +# print(f"package_name = '{package_name}'.") +# try: +# package = __import__(package_name) +# except ImportError: +# print("Package {} not found...".format(package_name)) +# sys.exit(1) +# +# print(f"package.__path__ = '{package.__path__}'.") +# print(f"package.__name__ = '{package.__name__}'.") +# +# all_modules = [] +# list_submodules(all_modules, package) +# +# print(f"all_modules = {all_modules}.") +# sys.exit(1) diff --git a/resources/lib/bottle_manager.py b/resources/lib/bottle_manager.py new file mode 100644 index 0000000..ab8dc19 --- /dev/null +++ b/resources/lib/bottle_manager.py @@ -0,0 +1,61 @@ +from wsgiref.simple_server import make_server + +from bottle import app, request, HTTPResponse, Bottle +import xbmc + +import re + +from librespot.core import Session +from librespot.audio.decoders import AudioQuality, VorbisOnlyAudioQuality +from librespot.metadata import TrackId + +from utils import log_msg + +class LibrespotServer(Bottle): + def __init__(self, session: Session): + super(LibrespotServer, self).__init__() + self.session: Session = session + self.route('/track/', callback=self.stream) + + # TODO: Make Range header work PLEASE I BEG + + def stream(self, track_id): + try: + playabletrack_id = TrackId.from_uri(f"spotify:track:{track_id}") + stream = self.session.content_feeder().load( + playabletrack_id, VorbisOnlyAudioQuality(AudioQuality.NORMAL), False, + None) + start = 0 + end = stream.input_stream.size + payload = stream.input_stream.stream() + log_msg(stream.input_stream.size) + # reqrange = request.get_header("range") + # if reqrange is not None: + # range_search = re.search( + # "^bytes=(?P[0-9]+?)-(?P[0-9]+?)$", + # reqrange) + # if range_search is not None: + # start = int(range_search.group("start")) + # end = (int(range_search.group("end")) + # if int(range_search.group("end")) <= + # stream.input_stream.size else + # stream.input_stream.size) + # payload.skip(start) + # else: + # payload = stream + response = HTTPResponse(body=payload) + response.add_header('Content-Type', 'audio/ogg') + # response.add_header('Accept-Ranges', 'bytes') + # response.add_header("Content-Length", str(stream.input_stream.size).encode() if + # stream.input_stream.size == end else "{}-{}/{}" + # .format(start, end, + # stream.input_stream.size).encode()) + return response + except Exception as e: + log_msg(e) + +def start_thread(web_port: int) -> None: + httpd = make_server('127.0.0.1', web_port, app) + monitor = xbmc.Monitor() + while not monitor.abortRequested(): + httpd.handle_request() diff --git a/resources/lib/defusedxml/ElementTree.py b/resources/lib/defusedxml/ElementTree.py new file mode 100644 index 0000000..5ba765f --- /dev/null +++ b/resources/lib/defusedxml/ElementTree.py @@ -0,0 +1,154 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""Defused xml.etree.ElementTree facade +""" +from __future__ import print_function, absolute_import + +import sys +import warnings +from xml.etree.ElementTree import ParseError +from xml.etree.ElementTree import TreeBuilder as _TreeBuilder +from xml.etree.ElementTree import parse as _parse +from xml.etree.ElementTree import tostring + +from .common import PY3 + +if PY3: + import importlib +else: + from xml.etree.ElementTree import XMLParser as _XMLParser + from xml.etree.ElementTree import iterparse as _iterparse + + +from .common import ( + DTDForbidden, + EntitiesForbidden, + ExternalReferenceForbidden, + _generate_etree_functions, +) + +__origin__ = "xml.etree.ElementTree" + + +def _get_py3_cls(): + """Python 3.3 hides the pure Python code but defusedxml requires it. + + The code is based on test.support.import_fresh_module(). + """ + pymodname = "xml.etree.ElementTree" + cmodname = "_elementtree" + + pymod = sys.modules.pop(pymodname, None) + cmod = sys.modules.pop(cmodname, None) + + sys.modules[cmodname] = None + try: + pure_pymod = importlib.import_module(pymodname) + finally: + # restore module + sys.modules[pymodname] = pymod + if cmod is not None: + sys.modules[cmodname] = cmod + else: + sys.modules.pop(cmodname, None) + # restore attribute on original package + etree_pkg = sys.modules["xml.etree"] + if pymod is not None: + etree_pkg.ElementTree = pymod + elif hasattr(etree_pkg, "ElementTree"): + del etree_pkg.ElementTree + + _XMLParser = pure_pymod.XMLParser + _iterparse = pure_pymod.iterparse + # patch pure module to use ParseError from C extension + pure_pymod.ParseError = ParseError + + return _XMLParser, _iterparse + + +if PY3: + _XMLParser, _iterparse = _get_py3_cls() + + +_sentinel = object() + + +class DefusedXMLParser(_XMLParser): + def __init__( + self, + html=_sentinel, + target=None, + encoding=None, + forbid_dtd=False, + forbid_entities=True, + forbid_external=True, + ): + # Python 2.x old style class + _XMLParser.__init__(self, target=target, encoding=encoding) + if html is not _sentinel: + # the 'html' argument has been deprecated and ignored in all + # supported versions of Python. Python 3.8 finally removed it. + if html: + raise TypeError("'html=True' is no longer supported.") + else: + warnings.warn( + "'html' keyword argument is no longer supported. Pass " + "in arguments as keyword arguments.", + category=DeprecationWarning, + ) + + self.forbid_dtd = forbid_dtd + self.forbid_entities = forbid_entities + self.forbid_external = forbid_external + if PY3: + parser = self.parser + else: + parser = self._parser + if self.forbid_dtd: + parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl + if self.forbid_entities: + parser.EntityDeclHandler = self.defused_entity_decl + parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl + if self.forbid_external: + parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler + + def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset): + raise DTDForbidden(name, sysid, pubid) + + def defused_entity_decl( + self, name, is_parameter_entity, value, base, sysid, pubid, notation_name + ): + raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) + + def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): + # expat 1.2 + raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) # pragma: no cover + + def defused_external_entity_ref_handler(self, context, base, sysid, pubid): + raise ExternalReferenceForbidden(context, base, sysid, pubid) + + +# aliases +# XMLParse is a typo, keep it for backwards compatibility +XMLTreeBuilder = XMLParse = XMLParser = DefusedXMLParser + +parse, iterparse, fromstring = _generate_etree_functions( + DefusedXMLParser, _TreeBuilder, _parse, _iterparse +) +XML = fromstring + + +__all__ = [ + "ParseError", + "XML", + "XMLParse", + "XMLParser", + "XMLTreeBuilder", + "fromstring", + "iterparse", + "parse", + "tostring", +] diff --git a/resources/lib/defusedxml/__init__.py b/resources/lib/defusedxml/__init__.py new file mode 100644 index 0000000..4b5a230 --- /dev/null +++ b/resources/lib/defusedxml/__init__.py @@ -0,0 +1,67 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""Defuse XML bomb denial of service vulnerabilities +""" +from __future__ import print_function, absolute_import + +import warnings + +from .common import ( + DefusedXmlException, + DTDForbidden, + EntitiesForbidden, + ExternalReferenceForbidden, + NotSupportedError, + _apply_defusing, +) + + +def defuse_stdlib(): + """Monkey patch and defuse all stdlib packages + + :warning: The monkey patch is an EXPERIMETNAL feature. + """ + defused = {} + + with warnings.catch_warnings(): + from . import cElementTree + from . import ElementTree + from . import minidom + from . import pulldom + from . import sax + from . import expatbuilder + from . import expatreader + from . import xmlrpc + + xmlrpc.monkey_patch() + defused[xmlrpc] = None + + defused_mods = [ + cElementTree, + ElementTree, + minidom, + pulldom, + sax, + expatbuilder, + expatreader, + ] + + for defused_mod in defused_mods: + stdlib_mod = _apply_defusing(defused_mod) + defused[defused_mod] = stdlib_mod + + return defused + + +__version__ = "0.7.1" + +__all__ = [ + "DefusedXmlException", + "DTDForbidden", + "EntitiesForbidden", + "ExternalReferenceForbidden", + "NotSupportedError", +] diff --git a/resources/lib/defusedxml/cElementTree.py b/resources/lib/defusedxml/cElementTree.py new file mode 100644 index 0000000..84670c6 --- /dev/null +++ b/resources/lib/defusedxml/cElementTree.py @@ -0,0 +1,62 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""Defused xml.etree.cElementTree +""" +from __future__ import absolute_import + +import warnings + +from .common import _generate_etree_functions + +from xml.etree.cElementTree import TreeBuilder as _TreeBuilder +from xml.etree.cElementTree import parse as _parse +from xml.etree.cElementTree import tostring + +# iterparse from ElementTree! +from xml.etree.ElementTree import iterparse as _iterparse + +# This module is an alias for ElementTree just like xml.etree.cElementTree +from .ElementTree import ( + XML, + XMLParse, + XMLParser, + XMLTreeBuilder, + fromstring, + iterparse, + parse, + tostring, + DefusedXMLParser, + ParseError, +) + +__origin__ = "xml.etree.cElementTree" + + +warnings.warn( + "defusedxml.cElementTree is deprecated, import from defusedxml.ElementTree instead.", + category=DeprecationWarning, + stacklevel=2, +) + +# XMLParse is a typo, keep it for backwards compatibility +XMLTreeBuilder = XMLParse = XMLParser = DefusedXMLParser + +parse, iterparse, fromstring = _generate_etree_functions( + DefusedXMLParser, _TreeBuilder, _parse, _iterparse +) +XML = fromstring + +__all__ = [ + "ParseError", + "XML", + "XMLParse", + "XMLParser", + "XMLTreeBuilder", + "fromstring", + "iterparse", + "parse", + "tostring", +] diff --git a/resources/lib/defusedxml/common.py b/resources/lib/defusedxml/common.py new file mode 100644 index 0000000..5ceda1f --- /dev/null +++ b/resources/lib/defusedxml/common.py @@ -0,0 +1,129 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""Common constants, exceptions and helpe functions +""" +import sys +import xml.parsers.expat + +PY3 = sys.version_info[0] == 3 + +# Fail early when pyexpat is not installed correctly +if not hasattr(xml.parsers.expat, "ParserCreate"): + raise ImportError("pyexpat") # pragma: no cover + + +class DefusedXmlException(ValueError): + """Base exception""" + + def __repr__(self): + return str(self) + + +class DTDForbidden(DefusedXmlException): + """Document type definition is forbidden""" + + def __init__(self, name, sysid, pubid): + super(DTDForbidden, self).__init__() + self.name = name + self.sysid = sysid + self.pubid = pubid + + def __str__(self): + tpl = "DTDForbidden(name='{}', system_id={!r}, public_id={!r})" + return tpl.format(self.name, self.sysid, self.pubid) + + +class EntitiesForbidden(DefusedXmlException): + """Entity definition is forbidden""" + + def __init__(self, name, value, base, sysid, pubid, notation_name): + super(EntitiesForbidden, self).__init__() + self.name = name + self.value = value + self.base = base + self.sysid = sysid + self.pubid = pubid + self.notation_name = notation_name + + def __str__(self): + tpl = "EntitiesForbidden(name='{}', system_id={!r}, public_id={!r})" + return tpl.format(self.name, self.sysid, self.pubid) + + +class ExternalReferenceForbidden(DefusedXmlException): + """Resolving an external reference is forbidden""" + + def __init__(self, context, base, sysid, pubid): + super(ExternalReferenceForbidden, self).__init__() + self.context = context + self.base = base + self.sysid = sysid + self.pubid = pubid + + def __str__(self): + tpl = "ExternalReferenceForbidden(system_id='{}', public_id={})" + return tpl.format(self.sysid, self.pubid) + + +class NotSupportedError(DefusedXmlException): + """The operation is not supported""" + + +def _apply_defusing(defused_mod): + assert defused_mod is sys.modules[defused_mod.__name__] + stdlib_name = defused_mod.__origin__ + __import__(stdlib_name, {}, {}, ["*"]) + stdlib_mod = sys.modules[stdlib_name] + stdlib_names = set(dir(stdlib_mod)) + for name, obj in vars(defused_mod).items(): + if name.startswith("_") or name not in stdlib_names: + continue + setattr(stdlib_mod, name, obj) + return stdlib_mod + + +def _generate_etree_functions(DefusedXMLParser, _TreeBuilder, _parse, _iterparse): + """Factory for functions needed by etree, dependent on whether + cElementTree or ElementTree is used.""" + + def parse(source, parser=None, forbid_dtd=False, forbid_entities=True, forbid_external=True): + if parser is None: + parser = DefusedXMLParser( + target=_TreeBuilder(), + forbid_dtd=forbid_dtd, + forbid_entities=forbid_entities, + forbid_external=forbid_external, + ) + return _parse(source, parser) + + def iterparse( + source, + events=None, + parser=None, + forbid_dtd=False, + forbid_entities=True, + forbid_external=True, + ): + if parser is None: + parser = DefusedXMLParser( + target=_TreeBuilder(), + forbid_dtd=forbid_dtd, + forbid_entities=forbid_entities, + forbid_external=forbid_external, + ) + return _iterparse(source, events, parser) + + def fromstring(text, forbid_dtd=False, forbid_entities=True, forbid_external=True): + parser = DefusedXMLParser( + target=_TreeBuilder(), + forbid_dtd=forbid_dtd, + forbid_entities=forbid_entities, + forbid_external=forbid_external, + ) + parser.feed(text) + return parser.close() + + return parse, iterparse, fromstring diff --git a/resources/lib/defusedxml/expatbuilder.py b/resources/lib/defusedxml/expatbuilder.py new file mode 100644 index 0000000..7bfc57e --- /dev/null +++ b/resources/lib/defusedxml/expatbuilder.py @@ -0,0 +1,107 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""Defused xml.dom.expatbuilder +""" +from __future__ import print_function, absolute_import + +from xml.dom.expatbuilder import ExpatBuilder as _ExpatBuilder +from xml.dom.expatbuilder import Namespaces as _Namespaces + +from .common import DTDForbidden, EntitiesForbidden, ExternalReferenceForbidden + +__origin__ = "xml.dom.expatbuilder" + + +class DefusedExpatBuilder(_ExpatBuilder): + """Defused document builder""" + + def __init__( + self, options=None, forbid_dtd=False, forbid_entities=True, forbid_external=True + ): + _ExpatBuilder.__init__(self, options) + self.forbid_dtd = forbid_dtd + self.forbid_entities = forbid_entities + self.forbid_external = forbid_external + + def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset): + raise DTDForbidden(name, sysid, pubid) + + def defused_entity_decl( + self, name, is_parameter_entity, value, base, sysid, pubid, notation_name + ): + raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) + + def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): + # expat 1.2 + raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) # pragma: no cover + + def defused_external_entity_ref_handler(self, context, base, sysid, pubid): + raise ExternalReferenceForbidden(context, base, sysid, pubid) + + def install(self, parser): + _ExpatBuilder.install(self, parser) + + if self.forbid_dtd: + parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl + if self.forbid_entities: + # if self._options.entities: + parser.EntityDeclHandler = self.defused_entity_decl + parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl + if self.forbid_external: + parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler + + +class DefusedExpatBuilderNS(_Namespaces, DefusedExpatBuilder): + """Defused document builder that supports namespaces.""" + + def install(self, parser): + DefusedExpatBuilder.install(self, parser) + if self._options.namespace_declarations: + parser.StartNamespaceDeclHandler = self.start_namespace_decl_handler + + def reset(self): + DefusedExpatBuilder.reset(self) + self._initNamespaces() + + +def parse(file, namespaces=True, forbid_dtd=False, forbid_entities=True, forbid_external=True): + """Parse a document, returning the resulting Document node. + + 'file' may be either a file name or an open file object. + """ + if namespaces: + build_builder = DefusedExpatBuilderNS + else: + build_builder = DefusedExpatBuilder + builder = build_builder( + forbid_dtd=forbid_dtd, forbid_entities=forbid_entities, forbid_external=forbid_external + ) + + if isinstance(file, str): + fp = open(file, "rb") + try: + result = builder.parseFile(fp) + finally: + fp.close() + else: + result = builder.parseFile(file) + return result + + +def parseString( + string, namespaces=True, forbid_dtd=False, forbid_entities=True, forbid_external=True +): + """Parse a document from a string, returning the resulting + Document node. + """ + if namespaces: + build_builder = DefusedExpatBuilderNS + else: + build_builder = DefusedExpatBuilder + builder = build_builder( + forbid_dtd=forbid_dtd, forbid_entities=forbid_entities, forbid_external=forbid_external + ) + return builder.parseString(string) diff --git a/resources/lib/defusedxml/expatreader.py b/resources/lib/defusedxml/expatreader.py new file mode 100644 index 0000000..890e1d1 --- /dev/null +++ b/resources/lib/defusedxml/expatreader.py @@ -0,0 +1,61 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""Defused xml.sax.expatreader +""" +from __future__ import print_function, absolute_import + +from xml.sax.expatreader import ExpatParser as _ExpatParser + +from .common import DTDForbidden, EntitiesForbidden, ExternalReferenceForbidden + +__origin__ = "xml.sax.expatreader" + + +class DefusedExpatParser(_ExpatParser): + """Defused SAX driver for the pyexpat C module.""" + + def __init__( + self, + namespaceHandling=0, + bufsize=2 ** 16 - 20, + forbid_dtd=False, + forbid_entities=True, + forbid_external=True, + ): + _ExpatParser.__init__(self, namespaceHandling, bufsize) + self.forbid_dtd = forbid_dtd + self.forbid_entities = forbid_entities + self.forbid_external = forbid_external + + def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset): + raise DTDForbidden(name, sysid, pubid) + + def defused_entity_decl( + self, name, is_parameter_entity, value, base, sysid, pubid, notation_name + ): + raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) + + def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): + # expat 1.2 + raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) # pragma: no cover + + def defused_external_entity_ref_handler(self, context, base, sysid, pubid): + raise ExternalReferenceForbidden(context, base, sysid, pubid) + + def reset(self): + _ExpatParser.reset(self) + parser = self._parser + if self.forbid_dtd: + parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl + if self.forbid_entities: + parser.EntityDeclHandler = self.defused_entity_decl + parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl + if self.forbid_external: + parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler + + +def create_parser(*args, **kwargs): + return DefusedExpatParser(*args, **kwargs) diff --git a/resources/lib/defusedxml/lxml.py b/resources/lib/defusedxml/lxml.py new file mode 100644 index 0000000..99d5be9 --- /dev/null +++ b/resources/lib/defusedxml/lxml.py @@ -0,0 +1,153 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""DEPRECATED Example code for lxml.etree protection + +The code has NO protection against decompression bombs. +""" +from __future__ import print_function, absolute_import + +import threading +import warnings + +from lxml import etree as _etree + +from .common import DTDForbidden, EntitiesForbidden, NotSupportedError + +LXML3 = _etree.LXML_VERSION[0] >= 3 + +__origin__ = "lxml.etree" + +tostring = _etree.tostring + + +warnings.warn( + "defusedxml.lxml is no longer supported and will be removed in a future release.", + category=DeprecationWarning, + stacklevel=2, +) + + +class RestrictedElement(_etree.ElementBase): + """A restricted Element class that filters out instances of some classes""" + + __slots__ = () + # blacklist = (etree._Entity, etree._ProcessingInstruction, etree._Comment) + blacklist = _etree._Entity + + def _filter(self, iterator): + blacklist = self.blacklist + for child in iterator: + if isinstance(child, blacklist): + continue + yield child + + def __iter__(self): + iterator = super(RestrictedElement, self).__iter__() + return self._filter(iterator) + + def iterchildren(self, tag=None, reversed=False): + iterator = super(RestrictedElement, self).iterchildren(tag=tag, reversed=reversed) + return self._filter(iterator) + + def iter(self, tag=None, *tags): + iterator = super(RestrictedElement, self).iter(tag=tag, *tags) + return self._filter(iterator) + + def iterdescendants(self, tag=None, *tags): + iterator = super(RestrictedElement, self).iterdescendants(tag=tag, *tags) + return self._filter(iterator) + + def itersiblings(self, tag=None, preceding=False): + iterator = super(RestrictedElement, self).itersiblings(tag=tag, preceding=preceding) + return self._filter(iterator) + + def getchildren(self): + iterator = super(RestrictedElement, self).__iter__() + return list(self._filter(iterator)) + + def getiterator(self, tag=None): + iterator = super(RestrictedElement, self).getiterator(tag) + return self._filter(iterator) + + +class GlobalParserTLS(threading.local): + """Thread local context for custom parser instances""" + + parser_config = { + "resolve_entities": False, + # 'remove_comments': True, + # 'remove_pis': True, + } + + element_class = RestrictedElement + + def createDefaultParser(self): + parser = _etree.XMLParser(**self.parser_config) + element_class = self.element_class + if self.element_class is not None: + lookup = _etree.ElementDefaultClassLookup(element=element_class) + parser.set_element_class_lookup(lookup) + return parser + + def setDefaultParser(self, parser): + self._default_parser = parser + + def getDefaultParser(self): + parser = getattr(self, "_default_parser", None) + if parser is None: + parser = self.createDefaultParser() + self.setDefaultParser(parser) + return parser + + +_parser_tls = GlobalParserTLS() +getDefaultParser = _parser_tls.getDefaultParser + + +def check_docinfo(elementtree, forbid_dtd=False, forbid_entities=True): + """Check docinfo of an element tree for DTD and entity declarations + + The check for entity declarations needs lxml 3 or newer. lxml 2.x does + not support dtd.iterentities(). + """ + docinfo = elementtree.docinfo + if docinfo.doctype: + if forbid_dtd: + raise DTDForbidden(docinfo.doctype, docinfo.system_url, docinfo.public_id) + if forbid_entities and not LXML3: + # lxml < 3 has no iterentities() + raise NotSupportedError("Unable to check for entity declarations " "in lxml 2.x") + + if forbid_entities: + for dtd in docinfo.internalDTD, docinfo.externalDTD: + if dtd is None: + continue + for entity in dtd.iterentities(): + raise EntitiesForbidden(entity.name, entity.content, None, None, None, None) + + +def parse(source, parser=None, base_url=None, forbid_dtd=False, forbid_entities=True): + if parser is None: + parser = getDefaultParser() + elementtree = _etree.parse(source, parser, base_url=base_url) + check_docinfo(elementtree, forbid_dtd, forbid_entities) + return elementtree + + +def fromstring(text, parser=None, base_url=None, forbid_dtd=False, forbid_entities=True): + if parser is None: + parser = getDefaultParser() + rootelement = _etree.fromstring(text, parser, base_url=base_url) + elementtree = rootelement.getroottree() + check_docinfo(elementtree, forbid_dtd, forbid_entities) + return rootelement + + +XML = fromstring + + +def iterparse(*args, **kwargs): + raise NotSupportedError("defused lxml.etree.iterparse not available") diff --git a/resources/lib/defusedxml/minidom.py b/resources/lib/defusedxml/minidom.py new file mode 100644 index 0000000..78033b6 --- /dev/null +++ b/resources/lib/defusedxml/minidom.py @@ -0,0 +1,63 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""Defused xml.dom.minidom +""" +from __future__ import print_function, absolute_import + +from xml.dom.minidom import _do_pulldom_parse +from . import expatbuilder as _expatbuilder +from . import pulldom as _pulldom + +__origin__ = "xml.dom.minidom" + + +def parse( + file, parser=None, bufsize=None, forbid_dtd=False, forbid_entities=True, forbid_external=True +): + """Parse a file into a DOM by filename or file object.""" + if parser is None and not bufsize: + return _expatbuilder.parse( + file, + forbid_dtd=forbid_dtd, + forbid_entities=forbid_entities, + forbid_external=forbid_external, + ) + else: + return _do_pulldom_parse( + _pulldom.parse, + (file,), + { + "parser": parser, + "bufsize": bufsize, + "forbid_dtd": forbid_dtd, + "forbid_entities": forbid_entities, + "forbid_external": forbid_external, + }, + ) + + +def parseString( + string, parser=None, forbid_dtd=False, forbid_entities=True, forbid_external=True +): + """Parse a file into a DOM from a string.""" + if parser is None: + return _expatbuilder.parseString( + string, + forbid_dtd=forbid_dtd, + forbid_entities=forbid_entities, + forbid_external=forbid_external, + ) + else: + return _do_pulldom_parse( + _pulldom.parseString, + (string,), + { + "parser": parser, + "forbid_dtd": forbid_dtd, + "forbid_entities": forbid_entities, + "forbid_external": forbid_external, + }, + ) diff --git a/resources/lib/defusedxml/pulldom.py b/resources/lib/defusedxml/pulldom.py new file mode 100644 index 0000000..e3b10a4 --- /dev/null +++ b/resources/lib/defusedxml/pulldom.py @@ -0,0 +1,41 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""Defused xml.dom.pulldom +""" +from __future__ import print_function, absolute_import + +from xml.dom.pulldom import parse as _parse +from xml.dom.pulldom import parseString as _parseString +from .sax import make_parser + +__origin__ = "xml.dom.pulldom" + + +def parse( + stream_or_string, + parser=None, + bufsize=None, + forbid_dtd=False, + forbid_entities=True, + forbid_external=True, +): + if parser is None: + parser = make_parser() + parser.forbid_dtd = forbid_dtd + parser.forbid_entities = forbid_entities + parser.forbid_external = forbid_external + return _parse(stream_or_string, parser, bufsize) + + +def parseString( + string, parser=None, forbid_dtd=False, forbid_entities=True, forbid_external=True +): + if parser is None: + parser = make_parser() + parser.forbid_dtd = forbid_dtd + parser.forbid_entities = forbid_entities + parser.forbid_external = forbid_external + return _parseString(string, parser) diff --git a/resources/lib/defusedxml/sax.py b/resources/lib/defusedxml/sax.py new file mode 100644 index 0000000..b2786f7 --- /dev/null +++ b/resources/lib/defusedxml/sax.py @@ -0,0 +1,60 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""Defused xml.sax +""" +from __future__ import print_function, absolute_import + +from xml.sax import InputSource as _InputSource +from xml.sax import ErrorHandler as _ErrorHandler + +from . import expatreader + +__origin__ = "xml.sax" + + +def parse( + source, + handler, + errorHandler=_ErrorHandler(), + forbid_dtd=False, + forbid_entities=True, + forbid_external=True, +): + parser = make_parser() + parser.setContentHandler(handler) + parser.setErrorHandler(errorHandler) + parser.forbid_dtd = forbid_dtd + parser.forbid_entities = forbid_entities + parser.forbid_external = forbid_external + parser.parse(source) + + +def parseString( + string, + handler, + errorHandler=_ErrorHandler(), + forbid_dtd=False, + forbid_entities=True, + forbid_external=True, +): + from io import BytesIO + + if errorHandler is None: + errorHandler = _ErrorHandler() + parser = make_parser() + parser.setContentHandler(handler) + parser.setErrorHandler(errorHandler) + parser.forbid_dtd = forbid_dtd + parser.forbid_entities = forbid_entities + parser.forbid_external = forbid_external + + inpsrc = _InputSource() + inpsrc.setByteStream(BytesIO(string)) + parser.parse(inpsrc) + + +def make_parser(parser_list=[]): + return expatreader.create_parser() diff --git a/resources/lib/defusedxml/xmlrpc.py b/resources/lib/defusedxml/xmlrpc.py new file mode 100644 index 0000000..fbc674d --- /dev/null +++ b/resources/lib/defusedxml/xmlrpc.py @@ -0,0 +1,153 @@ +# defusedxml +# +# Copyright (c) 2013 by Christian Heimes +# Licensed to PSF under a Contributor Agreement. +# See https://www.python.org/psf/license for licensing details. +"""Defused xmlrpclib + +Also defuses gzip bomb +""" +from __future__ import print_function, absolute_import + +import io + +from .common import DTDForbidden, EntitiesForbidden, ExternalReferenceForbidden, PY3 + +if PY3: + __origin__ = "xmlrpc.client" + from xmlrpc.client import ExpatParser + from xmlrpc import client as xmlrpc_client + from xmlrpc import server as xmlrpc_server + from xmlrpc.client import gzip_decode as _orig_gzip_decode + from xmlrpc.client import GzipDecodedResponse as _OrigGzipDecodedResponse +else: + __origin__ = "xmlrpclib" + from xmlrpclib import ExpatParser + import xmlrpclib as xmlrpc_client + + xmlrpc_server = None + from xmlrpclib import gzip_decode as _orig_gzip_decode + from xmlrpclib import GzipDecodedResponse as _OrigGzipDecodedResponse + +try: + import gzip +except ImportError: # pragma: no cover + gzip = None + + +# Limit maximum request size to prevent resource exhaustion DoS +# Also used to limit maximum amount of gzip decoded data in order to prevent +# decompression bombs +# A value of -1 or smaller disables the limit +MAX_DATA = 30 * 1024 * 1024 # 30 MB + + +def defused_gzip_decode(data, limit=None): + """gzip encoded data -> unencoded data + + Decode data using the gzip content encoding as described in RFC 1952 + """ + if not gzip: # pragma: no cover + raise NotImplementedError + if limit is None: + limit = MAX_DATA + f = io.BytesIO(data) + gzf = gzip.GzipFile(mode="rb", fileobj=f) + try: + if limit < 0: # no limit + decoded = gzf.read() + else: + decoded = gzf.read(limit + 1) + except IOError: # pragma: no cover + raise ValueError("invalid data") + f.close() + gzf.close() + if limit >= 0 and len(decoded) > limit: + raise ValueError("max gzipped payload length exceeded") + return decoded + + +class DefusedGzipDecodedResponse(gzip.GzipFile if gzip else object): + """a file-like object to decode a response encoded with the gzip + method, as described in RFC 1952. + """ + + def __init__(self, response, limit=None): + # response doesn't support tell() and read(), required by + # GzipFile + if not gzip: # pragma: no cover + raise NotImplementedError + self.limit = limit = limit if limit is not None else MAX_DATA + if limit < 0: # no limit + data = response.read() + self.readlength = None + else: + data = response.read(limit + 1) + self.readlength = 0 + if limit >= 0 and len(data) > limit: + raise ValueError("max payload length exceeded") + self.stringio = io.BytesIO(data) + gzip.GzipFile.__init__(self, mode="rb", fileobj=self.stringio) + + def read(self, n): + if self.limit >= 0: + left = self.limit - self.readlength + n = min(n, left + 1) + data = gzip.GzipFile.read(self, n) + self.readlength += len(data) + if self.readlength > self.limit: + raise ValueError("max payload length exceeded") + return data + else: + return gzip.GzipFile.read(self, n) + + def close(self): + gzip.GzipFile.close(self) + self.stringio.close() + + +class DefusedExpatParser(ExpatParser): + def __init__(self, target, forbid_dtd=False, forbid_entities=True, forbid_external=True): + ExpatParser.__init__(self, target) + self.forbid_dtd = forbid_dtd + self.forbid_entities = forbid_entities + self.forbid_external = forbid_external + parser = self._parser + if self.forbid_dtd: + parser.StartDoctypeDeclHandler = self.defused_start_doctype_decl + if self.forbid_entities: + parser.EntityDeclHandler = self.defused_entity_decl + parser.UnparsedEntityDeclHandler = self.defused_unparsed_entity_decl + if self.forbid_external: + parser.ExternalEntityRefHandler = self.defused_external_entity_ref_handler + + def defused_start_doctype_decl(self, name, sysid, pubid, has_internal_subset): + raise DTDForbidden(name, sysid, pubid) + + def defused_entity_decl( + self, name, is_parameter_entity, value, base, sysid, pubid, notation_name + ): + raise EntitiesForbidden(name, value, base, sysid, pubid, notation_name) + + def defused_unparsed_entity_decl(self, name, base, sysid, pubid, notation_name): + # expat 1.2 + raise EntitiesForbidden(name, None, base, sysid, pubid, notation_name) # pragma: no cover + + def defused_external_entity_ref_handler(self, context, base, sysid, pubid): + raise ExternalReferenceForbidden(context, base, sysid, pubid) + + +def monkey_patch(): + xmlrpc_client.FastParser = DefusedExpatParser + xmlrpc_client.GzipDecodedResponse = DefusedGzipDecodedResponse + xmlrpc_client.gzip_decode = defused_gzip_decode + if xmlrpc_server: + xmlrpc_server.gzip_decode = defused_gzip_decode + + +def unmonkey_patch(): + xmlrpc_client.FastParser = None + xmlrpc_client.GzipDecodedResponse = _OrigGzipDecodedResponse + xmlrpc_client.gzip_decode = _orig_gzip_decode + if xmlrpc_server: + xmlrpc_server.gzip_decode = _orig_gzip_decode diff --git a/resources/lib/deps/__init__.py b/resources/lib/deps/__init__.py new file mode 100644 index 0000000..35db612 --- /dev/null +++ b/resources/lib/deps/__init__.py @@ -0,0 +1,4 @@ +import os +import sys + +sys.path.insert(1, os.path.join(os.path.dirname(__file__))) diff --git a/resources/lib/deps/bottle.py b/resources/lib/deps/bottle.py new file mode 100755 index 0000000..916e260 --- /dev/null +++ b/resources/lib/deps/bottle.py @@ -0,0 +1,4417 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +Bottle is a fast and simple micro-framework for small web applications. It +offers request dispatching (Routes) with URL parameter support, templates, +a built-in HTTP Server and adapters for many third party WSGI/HTTP-server and +template engines - all in a single file and with no dependencies other than the +Python Standard Library. + +Homepage and documentation: http://bottlepy.org/ + +Copyright (c) 2009-2018, Marcel Hellkamp. +License: MIT (see LICENSE for details) +""" + +from __future__ import print_function +import sys + +__author__ = 'Marcel Hellkamp' +__version__ = '0.13-dev' +__license__ = 'MIT' + +############################################################################### +# Command-line interface ###################################################### +############################################################################### +# INFO: Some server adapters need to monkey-patch std-lib modules before they +# are imported. This is why some of the command-line handling is done here, but +# the actual call to _main() is at the end of the file. + + +def _cli_parse(args): # pragma: no coverage + from argparse import ArgumentParser + + parser = ArgumentParser(prog=args[0], usage="%(prog)s [options] package.module:app") + opt = parser.add_argument + opt("--version", action="store_true", help="show version number.") + opt("-b", "--bind", metavar="ADDRESS", help="bind socket to ADDRESS.") + opt("-s", "--server", default='wsgiref', help="use SERVER as backend.") + opt("-p", "--plugin", action="append", help="install additional plugin/s.") + opt("-c", "--conf", action="append", metavar="FILE", + help="load config values from FILE.") + opt("-C", "--param", action="append", metavar="NAME=VALUE", + help="override config values.") + opt("--debug", action="store_true", help="start server in debug mode.") + opt("--reload", action="store_true", help="auto-reload on file changes.") + opt('app', help='WSGI app entry point.', nargs='?') + + cli_args = parser.parse_args(args[1:]) + + return cli_args, parser + + +def _cli_patch(cli_args): # pragma: no coverage + parsed_args, _ = _cli_parse(cli_args) + opts = parsed_args + if opts.server: + if opts.server.startswith('gevent'): + import gevent.monkey + gevent.monkey.patch_all() + elif opts.server.startswith('eventlet'): + import eventlet + eventlet.monkey_patch() + + +if __name__ == '__main__': + _cli_patch(sys.argv) + +############################################################################### +# Imports and Python 2/3 unification ########################################## +############################################################################### + +import base64, calendar, cgi, email.utils, functools, hmac, itertools,\ + mimetypes, os, re, tempfile, threading, time, warnings, weakref, hashlib + +from types import FunctionType +from datetime import date as datedate, datetime, timedelta +from tempfile import NamedTemporaryFile +from traceback import format_exc, print_exc +from unicodedata import normalize + +try: + from ujson import dumps as json_dumps, loads as json_lds +except ImportError: + from json import dumps as json_dumps, loads as json_lds + +py = sys.version_info +py3k = py.major > 2 + +# Lots of stdlib and builtin differences. +if py3k: + import http.client as httplib + import _thread as thread + from urllib.parse import urljoin, SplitResult as UrlSplitResult + from urllib.parse import urlencode, quote as urlquote, unquote as urlunquote + urlunquote = functools.partial(urlunquote, encoding='latin1') + from http.cookies import SimpleCookie, Morsel, CookieError + from collections.abc import MutableMapping as DictMixin + from types import ModuleType as new_module + import pickle + from io import BytesIO + import configparser + # getfullargspec was deprecated in 3.5 and un-deprecated in 3.6 + # getargspec was deprecated in 3.0 and removed in 3.11 + from inspect import getfullargspec + def getargspec(func): + spec = getfullargspec(func) + kwargs = makelist(spec[0]) + makelist(spec.kwonlyargs) + return kwargs, spec[1], spec[2], spec[3] + + basestring = str + unicode = str + json_loads = lambda s: json_lds(touni(s)) + callable = lambda x: hasattr(x, '__call__') + imap = map + + def _raise(*a): + raise a[0](a[1]).with_traceback(a[2]) +else: # 2.x + import httplib + import thread + from urlparse import urljoin, SplitResult as UrlSplitResult + from urllib import urlencode, quote as urlquote, unquote as urlunquote + from Cookie import SimpleCookie, Morsel, CookieError + from itertools import imap + import cPickle as pickle + from imp import new_module + from StringIO import StringIO as BytesIO + import ConfigParser as configparser + from collections import MutableMapping as DictMixin + from inspect import getargspec + + unicode = unicode + json_loads = json_lds + exec(compile('def _raise(*a): raise a[0], a[1], a[2]', '', 'exec')) + +# Some helpers for string/byte handling +def tob(s, enc='utf8'): + if isinstance(s, unicode): + return s.encode(enc) + return b'' if s is None else bytes(s) + + +def touni(s, enc='utf8', err='strict'): + if isinstance(s, bytes): + return s.decode(enc, err) + return unicode("" if s is None else s) + + +tonat = touni if py3k else tob + + +def _stderr(*args): + try: + print(*args, file=sys.stderr) + except (IOError, AttributeError): + pass # Some environments do not allow printing (mod_wsgi) + + +# A bug in functools causes it to break if the wrapper is an instance method +def update_wrapper(wrapper, wrapped, *a, **ka): + try: + functools.update_wrapper(wrapper, wrapped, *a, **ka) + except AttributeError: + pass + +# These helpers are used at module level and need to be defined first. +# And yes, I know PEP-8, but sometimes a lower-case classname makes more sense. + + +def depr(major, minor, cause, fix): + text = "Warning: Use of deprecated feature or API. (Deprecated in Bottle-%d.%d)\n"\ + "Cause: %s\n"\ + "Fix: %s\n" % (major, minor, cause, fix) + if DEBUG == 'strict': + raise DeprecationWarning(text) + warnings.warn(text, DeprecationWarning, stacklevel=3) + return DeprecationWarning(text) + + +def makelist(data): # This is just too handy + if isinstance(data, (tuple, list, set, dict)): + return list(data) + elif data: + return [data] + else: + return [] + + +class DictProperty(object): + """ Property that maps to a key in a local dict-like attribute. """ + + def __init__(self, attr, key=None, read_only=False): + self.attr, self.key, self.read_only = attr, key, read_only + + def __call__(self, func): + functools.update_wrapper(self, func, updated=[]) + self.getter, self.key = func, self.key or func.__name__ + return self + + def __get__(self, obj, cls): + if obj is None: return self + key, storage = self.key, getattr(obj, self.attr) + if key not in storage: storage[key] = self.getter(obj) + return storage[key] + + def __set__(self, obj, value): + if self.read_only: raise AttributeError("Read-Only property.") + getattr(obj, self.attr)[self.key] = value + + def __delete__(self, obj): + if self.read_only: raise AttributeError("Read-Only property.") + del getattr(obj, self.attr)[self.key] + + +class cached_property(object): + """ A property that is only computed once per instance and then replaces + itself with an ordinary attribute. Deleting the attribute resets the + property. """ + + def __init__(self, func): + update_wrapper(self, func) + self.func = func + + def __get__(self, obj, cls): + if obj is None: return self + value = obj.__dict__[self.func.__name__] = self.func(obj) + return value + + +class lazy_attribute(object): + """ A property that caches itself to the class object. """ + + def __init__(self, func): + functools.update_wrapper(self, func, updated=[]) + self.getter = func + + def __get__(self, obj, cls): + value = self.getter(cls) + setattr(cls, self.__name__, value) + return value + + +############################################################################### +# Exceptions and Events ####################################################### +############################################################################### + + +class BottleException(Exception): + """ A base class for exceptions used by bottle. """ + pass + +############################################################################### +# Routing ###################################################################### +############################################################################### + + +class RouteError(BottleException): + """ This is a base class for all routing related exceptions """ + + +class RouteReset(BottleException): + """ If raised by a plugin or request handler, the route is reset and all + plugins are re-applied. """ + + +class RouterUnknownModeError(RouteError): + + pass + + +class RouteSyntaxError(RouteError): + """ The route parser found something not supported by this router. """ + + +class RouteBuildError(RouteError): + """ The route could not be built. """ + + +def _re_flatten(p): + """ Turn all capturing groups in a regular expression pattern into + non-capturing groups. """ + if '(' not in p: + return p + return re.sub(r'(\\*)(\(\?P<[^>]+>|\((?!\?))', lambda m: m.group(0) if + len(m.group(1)) % 2 else m.group(1) + '(?:', p) + + +class Router(object): + """ A Router is an ordered collection of route->target pairs. It is used to + efficiently match WSGI requests against a number of routes and return + the first target that satisfies the request. The target may be anything, + usually a string, ID or callable object. A route consists of a path-rule + and a HTTP method. + + The path-rule is either a static path (e.g. `/contact`) or a dynamic + path that contains wildcards (e.g. `/wiki/`). The wildcard syntax + and details on the matching order are described in docs:`routing`. + """ + + default_pattern = '[^/]+' + default_filter = 're' + + #: The current CPython regexp implementation does not allow more + #: than 99 matching groups per regular expression. + _MAX_GROUPS_PER_PATTERN = 99 + + def __init__(self, strict=False): + self.rules = [] # All rules in order + self._groups = {} # index of regexes to find them in dyna_routes + self.builder = {} # Data structure for the url builder + self.static = {} # Search structure for static routes + self.dyna_routes = {} + self.dyna_regexes = {} # Search structure for dynamic routes + #: If true, static routes are no longer checked first. + self.strict_order = strict + self.filters = { + 're': lambda conf: (_re_flatten(conf or self.default_pattern), + None, None), + 'int': lambda conf: (r'-?\d+', int, lambda x: str(int(x))), + 'float': lambda conf: (r'-?[\d.]+', float, lambda x: str(float(x))), + 'path': lambda conf: (r'.+?', None, None) + } + + def add_filter(self, name, func): + """ Add a filter. The provided function is called with the configuration + string as parameter and must return a (regexp, to_python, to_url) tuple. + The first element is a string, the last two are callables or None. """ + self.filters[name] = func + + rule_syntax = re.compile('(\\\\*)' + '(?:(?::([a-zA-Z_][a-zA-Z_0-9]*)?()(?:#(.*?)#)?)' + '|(?:<([a-zA-Z_][a-zA-Z_0-9]*)?(?::([a-zA-Z_]*)' + '(?::((?:\\\\.|[^\\\\>])+)?)?)?>))') + + def _itertokens(self, rule): + offset, prefix = 0, '' + for match in self.rule_syntax.finditer(rule): + prefix += rule[offset:match.start()] + g = match.groups() + if g[2] is not None: + depr(0, 13, "Use of old route syntax.", + "Use instead of :name in routes.") + if len(g[0]) % 2: # Escaped wildcard + prefix += match.group(0)[len(g[0]):] + offset = match.end() + continue + if prefix: + yield prefix, None, None + name, filtr, conf = g[4:7] if g[2] is None else g[1:4] + yield name, filtr or 'default', conf or None + offset, prefix = match.end(), '' + if offset <= len(rule) or prefix: + yield prefix + rule[offset:], None, None + + def add(self, rule, method, target, name=None): + """ Add a new rule or replace the target for an existing rule. """ + anons = 0 # Number of anonymous wildcards found + keys = [] # Names of keys + pattern = '' # Regular expression pattern with named groups + filters = [] # Lists of wildcard input filters + builder = [] # Data structure for the URL builder + is_static = True + + for key, mode, conf in self._itertokens(rule): + if mode: + is_static = False + if mode == 'default': mode = self.default_filter + mask, in_filter, out_filter = self.filters[mode](conf) + if not key: + pattern += '(?:%s)' % mask + key = 'anon%d' % anons + anons += 1 + else: + pattern += '(?P<%s>%s)' % (key, mask) + keys.append(key) + if in_filter: filters.append((key, in_filter)) + builder.append((key, out_filter or str)) + elif key: + pattern += re.escape(key) + builder.append((None, key)) + + self.builder[rule] = builder + if name: self.builder[name] = builder + + if is_static and not self.strict_order: + self.static.setdefault(method, {}) + self.static[method][self.build(rule)] = (target, None) + return + + try: + re_pattern = re.compile('^(%s)$' % pattern) + re_match = re_pattern.match + except re.error as e: + raise RouteSyntaxError("Could not add Route: %s (%s)" % (rule, e)) + + if filters: + + def getargs(path): + url_args = re_match(path).groupdict() + for name, wildcard_filter in filters: + try: + url_args[name] = wildcard_filter(url_args[name]) + except ValueError: + raise HTTPError(400, 'Path has wrong format.') + return url_args + elif re_pattern.groupindex: + + def getargs(path): + return re_match(path).groupdict() + else: + getargs = None + + flatpat = _re_flatten(pattern) + whole_rule = (rule, flatpat, target, getargs) + + if (flatpat, method) in self._groups: + if DEBUG: + msg = 'Route <%s %s> overwrites a previously defined route' + warnings.warn(msg % (method, rule), RuntimeWarning) + self.dyna_routes[method][ + self._groups[flatpat, method]] = whole_rule + else: + self.dyna_routes.setdefault(method, []).append(whole_rule) + self._groups[flatpat, method] = len(self.dyna_routes[method]) - 1 + + self._compile(method) + + def _compile(self, method): + all_rules = self.dyna_routes[method] + comborules = self.dyna_regexes[method] = [] + maxgroups = self._MAX_GROUPS_PER_PATTERN + for x in range(0, len(all_rules), maxgroups): + some = all_rules[x:x + maxgroups] + combined = (flatpat for (_, flatpat, _, _) in some) + combined = '|'.join('(^%s$)' % flatpat for flatpat in combined) + combined = re.compile(combined).match + rules = [(target, getargs) for (_, _, target, getargs) in some] + comborules.append((combined, rules)) + + def build(self, _name, *anons, **query): + """ Build an URL by filling the wildcards in a rule. """ + builder = self.builder.get(_name) + if not builder: + raise RouteBuildError("No route with that name.", _name) + try: + for i, value in enumerate(anons): + query['anon%d' % i] = value + url = ''.join([f(query.pop(n)) if n else f for (n, f) in builder]) + return url if not query else url + '?' + urlencode(query) + except KeyError as E: + raise RouteBuildError('Missing URL argument: %r' % E.args[0]) + + def match(self, environ): + """ Return a (target, url_args) tuple or raise HTTPError(400/404/405). """ + verb = environ['REQUEST_METHOD'].upper() + path = environ['PATH_INFO'] or '/' + + methods = ('PROXY', 'HEAD', 'GET', 'ANY') if verb == 'HEAD' else ('PROXY', verb, 'ANY') + + for method in methods: + if method in self.static and path in self.static[method]: + target, getargs = self.static[method][path] + return target, getargs(path) if getargs else {} + elif method in self.dyna_regexes: + for combined, rules in self.dyna_regexes[method]: + match = combined(path) + if match: + target, getargs = rules[match.lastindex - 1] + return target, getargs(path) if getargs else {} + + # No matching route found. Collect alternative methods for 405 response + allowed = set([]) + nocheck = set(methods) + for method in set(self.static) - nocheck: + if path in self.static[method]: + allowed.add(method) + for method in set(self.dyna_regexes) - allowed - nocheck: + for combined, rules in self.dyna_regexes[method]: + match = combined(path) + if match: + allowed.add(method) + if allowed: + allow_header = ",".join(sorted(allowed)) + raise HTTPError(405, "Method not allowed.", Allow=allow_header) + + # No matching route and no alternative method found. We give up + raise HTTPError(404, "Not found: " + repr(path)) + + +class Route(object): + """ This class wraps a route callback along with route specific metadata and + configuration and applies Plugins on demand. It is also responsible for + turning an URL path rule into a regular expression usable by the Router. + """ + + def __init__(self, app, rule, method, callback, + name=None, + plugins=None, + skiplist=None, **config): + #: The application this route is installed to. + self.app = app + #: The path-rule string (e.g. ``/wiki/``). + self.rule = rule + #: The HTTP method as a string (e.g. ``GET``). + self.method = method + #: The original callback with no plugins applied. Useful for introspection. + self.callback = callback + #: The name of the route (if specified) or ``None``. + self.name = name or None + #: A list of route-specific plugins (see :meth:`Bottle.route`). + self.plugins = plugins or [] + #: A list of plugins to not apply to this route (see :meth:`Bottle.route`). + self.skiplist = skiplist or [] + #: Additional keyword arguments passed to the :meth:`Bottle.route` + #: decorator are stored in this dictionary. Used for route-specific + #: plugin configuration and meta-data. + self.config = app.config._make_overlay() + self.config.load_dict(config) + + @cached_property + def call(self): + """ The route callback with all plugins applied. This property is + created on demand and then cached to speed up subsequent requests.""" + return self._make_callback() + + def reset(self): + """ Forget any cached values. The next time :attr:`call` is accessed, + all plugins are re-applied. """ + self.__dict__.pop('call', None) + + def prepare(self): + """ Do all on-demand work immediately (useful for debugging).""" + self.call + + def all_plugins(self): + """ Yield all Plugins affecting this route. """ + unique = set() + for p in reversed(self.app.plugins + self.plugins): + if True in self.skiplist: break + name = getattr(p, 'name', False) + if name and (name in self.skiplist or name in unique): continue + if p in self.skiplist or type(p) in self.skiplist: continue + if name: unique.add(name) + yield p + + def _make_callback(self): + callback = self.callback + for plugin in self.all_plugins(): + try: + if hasattr(plugin, 'apply'): + callback = plugin.apply(callback, self) + else: + callback = plugin(callback) + except RouteReset: # Try again with changed configuration. + return self._make_callback() + if callback is not self.callback: + update_wrapper(callback, self.callback) + return callback + + def get_undecorated_callback(self): + """ Return the callback. If the callback is a decorated function, try to + recover the original function. """ + func = self.callback + func = getattr(func, '__func__' if py3k else 'im_func', func) + closure_attr = '__closure__' if py3k else 'func_closure' + while hasattr(func, closure_attr) and getattr(func, closure_attr): + attributes = getattr(func, closure_attr) + func = attributes[0].cell_contents + + # in case of decorators with multiple arguments + if not isinstance(func, FunctionType): + # pick first FunctionType instance from multiple arguments + func = filter(lambda x: isinstance(x, FunctionType), + map(lambda x: x.cell_contents, attributes)) + func = list(func)[0] # py3 support + return func + + def get_callback_args(self): + """ Return a list of argument names the callback (most likely) accepts + as keyword arguments. If the callback is a decorated function, try + to recover the original function before inspection. """ + return getargspec(self.get_undecorated_callback())[0] + + def get_config(self, key, default=None): + """ Lookup a config field and return its value, first checking the + route.config, then route.app.config.""" + depr(0, 13, "Route.get_config() is deprecated.", + "The Route.config property already includes values from the" + " application config for missing keys. Access it directly.") + return self.config.get(key, default) + + def __repr__(self): + cb = self.get_undecorated_callback() + return '<%s %s -> %s:%s>' % (self.method, self.rule, cb.__module__, cb.__name__) + +############################################################################### +# Application Object ########################################################### +############################################################################### + + +class Bottle(object): + """ Each Bottle object represents a single, distinct web application and + consists of routes, callbacks, plugins, resources and configuration. + Instances are callable WSGI applications. + + :param catchall: If true (default), handle all exceptions. Turn off to + let debugging middleware handle exceptions. + """ + + @lazy_attribute + def _global_config(cls): + cfg = ConfigDict() + cfg.meta_set('catchall', 'validate', bool) + return cfg + + def __init__(self, **kwargs): + #: A :class:`ConfigDict` for app specific configuration. + self.config = self._global_config._make_overlay() + self.config._add_change_listener( + functools.partial(self.trigger_hook, 'config')) + + self.config.update({ + "catchall": True + }) + + if kwargs.get('catchall') is False: + depr(0, 13, "Bottle(catchall) keyword argument.", + "The 'catchall' setting is now part of the app " + "configuration. Fix: `app.config['catchall'] = False`") + self.config['catchall'] = False + if kwargs.get('autojson') is False: + depr(0, 13, "Bottle(autojson) keyword argument.", + "The 'autojson' setting is now part of the app " + "configuration. Fix: `app.config['json.enable'] = False`") + self.config['json.disable'] = True + + self._mounts = [] + + #: A :class:`ResourceManager` for application files + self.resources = ResourceManager() + + self.routes = [] # List of installed :class:`Route` instances. + self.router = Router() # Maps requests to :class:`Route` instances. + self.error_handler = {} + + # Core plugins + self.plugins = [] # List of installed plugins. + self.install(JSONPlugin()) + self.install(TemplatePlugin()) + + #: If true, most exceptions are caught and returned as :exc:`HTTPError` + catchall = DictProperty('config', 'catchall') + + __hook_names = 'before_request', 'after_request', 'app_reset', 'config' + __hook_reversed = {'after_request'} + + @cached_property + def _hooks(self): + return dict((name, []) for name in self.__hook_names) + + def add_hook(self, name, func): + """ Attach a callback to a hook. Three hooks are currently implemented: + + before_request + Executed once before each request. The request context is + available, but no routing has happened yet. + after_request + Executed once after each request regardless of its outcome. + app_reset + Called whenever :meth:`Bottle.reset` is called. + """ + if name in self.__hook_reversed: + self._hooks[name].insert(0, func) + else: + self._hooks[name].append(func) + + def remove_hook(self, name, func): + """ Remove a callback from a hook. """ + if name in self._hooks and func in self._hooks[name]: + self._hooks[name].remove(func) + return True + + def trigger_hook(self, __name, *args, **kwargs): + """ Trigger a hook and return a list of results. """ + return [hook(*args, **kwargs) for hook in self._hooks[__name][:]] + + def hook(self, name): + """ Return a decorator that attaches a callback to a hook. See + :meth:`add_hook` for details.""" + + def decorator(func): + self.add_hook(name, func) + return func + + return decorator + + def _mount_wsgi(self, prefix, app, **options): + segments = [p for p in prefix.split('/') if p] + if not segments: + raise ValueError('WSGI applications cannot be mounted to "/".') + path_depth = len(segments) + + def mountpoint_wrapper(): + try: + request.path_shift(path_depth) + rs = HTTPResponse([]) + + def start_response(status, headerlist, exc_info=None): + if exc_info: + _raise(*exc_info) + if py3k: + # Errors here mean that the mounted WSGI app did not + # follow PEP-3333 (which requires latin1) or used a + # pre-encoding other than utf8 :/ + status = status.encode('latin1').decode('utf8') + headerlist = [(k, v.encode('latin1').decode('utf8')) + for (k, v) in headerlist] + rs.status = status + for name, value in headerlist: + rs.add_header(name, value) + return rs.body.append + + body = app(request.environ, start_response) + rs.body = itertools.chain(rs.body, body) if rs.body else body + return rs + finally: + request.path_shift(-path_depth) + + options.setdefault('skip', True) + options.setdefault('method', 'PROXY') + options.setdefault('mountpoint', {'prefix': prefix, 'target': app}) + options['callback'] = mountpoint_wrapper + + self.route('/%s/<:re:.*>' % '/'.join(segments), **options) + if not prefix.endswith('/'): + self.route('/' + '/'.join(segments), **options) + + def _mount_app(self, prefix, app, **options): + if app in self._mounts or '_mount.app' in app.config: + depr(0, 13, "Application mounted multiple times. Falling back to WSGI mount.", + "Clone application before mounting to a different location.") + return self._mount_wsgi(prefix, app, **options) + + if options: + depr(0, 13, "Unsupported mount options. Falling back to WSGI mount.", + "Do not specify any route options when mounting bottle application.") + return self._mount_wsgi(prefix, app, **options) + + if not prefix.endswith("/"): + depr(0, 13, "Prefix must end in '/'. Falling back to WSGI mount.", + "Consider adding an explicit redirect from '/prefix' to '/prefix/' in the parent application.") + return self._mount_wsgi(prefix, app, **options) + + self._mounts.append(app) + app.config['_mount.prefix'] = prefix + app.config['_mount.app'] = self + for route in app.routes: + route.rule = prefix + route.rule.lstrip('/') + self.add_route(route) + + def mount(self, prefix, app, **options): + """ Mount an application (:class:`Bottle` or plain WSGI) to a specific + URL prefix. Example:: + + parent_app.mount('/prefix/', child_app) + + :param prefix: path prefix or `mount-point`. + :param app: an instance of :class:`Bottle` or a WSGI application. + + Plugins from the parent application are not applied to the routes + of the mounted child application. If you need plugins in the child + application, install them separately. + + While it is possible to use path wildcards within the prefix path + (:class:`Bottle` childs only), it is highly discouraged. + + The prefix path must end with a slash. If you want to access the + root of the child application via `/prefix` in addition to + `/prefix/`, consider adding a route with a 307 redirect to the + parent application. + """ + + if not prefix.startswith('/'): + raise ValueError("Prefix must start with '/'") + + if isinstance(app, Bottle): + return self._mount_app(prefix, app, **options) + else: + return self._mount_wsgi(prefix, app, **options) + + def merge(self, routes): + """ Merge the routes of another :class:`Bottle` application or a list of + :class:`Route` objects into this application. The routes keep their + 'owner', meaning that the :data:`Route.app` attribute is not + changed. """ + if isinstance(routes, Bottle): + routes = routes.routes + for route in routes: + self.add_route(route) + + def install(self, plugin): + """ Add a plugin to the list of plugins and prepare it for being + applied to all routes of this application. A plugin may be a simple + decorator or an object that implements the :class:`Plugin` API. + """ + if hasattr(plugin, 'setup'): plugin.setup(self) + if not callable(plugin) and not hasattr(plugin, 'apply'): + raise TypeError("Plugins must be callable or implement .apply()") + self.plugins.append(plugin) + self.reset() + return plugin + + def uninstall(self, plugin): + """ Uninstall plugins. Pass an instance to remove a specific plugin, a type + object to remove all plugins that match that type, a string to remove + all plugins with a matching ``name`` attribute or ``True`` to remove all + plugins. Return the list of removed plugins. """ + removed, remove = [], plugin + for i, plugin in list(enumerate(self.plugins))[::-1]: + if remove is True or remove is plugin or remove is type(plugin) \ + or getattr(plugin, 'name', True) == remove: + removed.append(plugin) + del self.plugins[i] + if hasattr(plugin, 'close'): plugin.close() + if removed: self.reset() + return removed + + def reset(self, route=None): + """ Reset all routes (force plugins to be re-applied) and clear all + caches. If an ID or route object is given, only that specific route + is affected. """ + if route is None: routes = self.routes + elif isinstance(route, Route): routes = [route] + else: routes = [self.routes[route]] + for route in routes: + route.reset() + if DEBUG: + for route in routes: + route.prepare() + self.trigger_hook('app_reset') + + def close(self): + """ Close the application and all installed plugins. """ + for plugin in self.plugins: + if hasattr(plugin, 'close'): plugin.close() + + def run(self, **kwargs): + """ Calls :func:`run` with the same parameters. """ + run(self, **kwargs) + + def match(self, environ): + """ Search for a matching route and return a (:class:`Route`, urlargs) + tuple. The second value is a dictionary with parameters extracted + from the URL. Raise :exc:`HTTPError` (404/405) on a non-match.""" + return self.router.match(environ) + + def get_url(self, routename, **kargs): + """ Return a string that matches a named route """ + scriptname = request.environ.get('SCRIPT_NAME', '').strip('/') + '/' + location = self.router.build(routename, **kargs).lstrip('/') + return urljoin(urljoin('/', scriptname), location) + + def add_route(self, route): + """ Add a route object, but do not change the :data:`Route.app` + attribute.""" + self.routes.append(route) + self.router.add(route.rule, route.method, route, name=route.name) + if DEBUG: route.prepare() + + def route(self, + path=None, + method='GET', + callback=None, + name=None, + apply=None, + skip=None, **config): + """ A decorator to bind a function to a request URL. Example:: + + @app.route('/hello/') + def hello(name): + return 'Hello %s' % name + + The ```` part is a wildcard. See :class:`Router` for syntax + details. + + :param path: Request path or a list of paths to listen to. If no + path is specified, it is automatically generated from the + signature of the function. + :param method: HTTP method (`GET`, `POST`, `PUT`, ...) or a list of + methods to listen to. (default: `GET`) + :param callback: An optional shortcut to avoid the decorator + syntax. ``route(..., callback=func)`` equals ``route(...)(func)`` + :param name: The name for this route. (default: None) + :param apply: A decorator or plugin or a list of plugins. These are + applied to the route callback in addition to installed plugins. + :param skip: A list of plugins, plugin classes or names. Matching + plugins are not installed to this route. ``True`` skips all. + + Any additional keyword arguments are stored as route-specific + configuration and passed to plugins (see :meth:`Plugin.apply`). + """ + if callable(path): path, callback = None, path + plugins = makelist(apply) + skiplist = makelist(skip) + + def decorator(callback): + if isinstance(callback, basestring): callback = load(callback) + for rule in makelist(path) or yieldroutes(callback): + for verb in makelist(method): + verb = verb.upper() + route = Route(self, rule, verb, callback, + name=name, + plugins=plugins, + skiplist=skiplist, **config) + self.add_route(route) + return callback + + return decorator(callback) if callback else decorator + + def get(self, path=None, method='GET', **options): + """ Equals :meth:`route`. """ + return self.route(path, method, **options) + + def post(self, path=None, method='POST', **options): + """ Equals :meth:`route` with a ``POST`` method parameter. """ + return self.route(path, method, **options) + + def put(self, path=None, method='PUT', **options): + """ Equals :meth:`route` with a ``PUT`` method parameter. """ + return self.route(path, method, **options) + + def delete(self, path=None, method='DELETE', **options): + """ Equals :meth:`route` with a ``DELETE`` method parameter. """ + return self.route(path, method, **options) + + def patch(self, path=None, method='PATCH', **options): + """ Equals :meth:`route` with a ``PATCH`` method parameter. """ + return self.route(path, method, **options) + + def error(self, code=500, callback=None): + """ Register an output handler for a HTTP error code. Can + be used as a decorator or called directly :: + + def error_handler_500(error): + return 'error_handler_500' + + app.error(code=500, callback=error_handler_500) + + @app.error(404) + def error_handler_404(error): + return 'error_handler_404' + + """ + + def decorator(callback): + if isinstance(callback, basestring): callback = load(callback) + self.error_handler[int(code)] = callback + return callback + + return decorator(callback) if callback else decorator + + def default_error_handler(self, res): + return tob(template(ERROR_PAGE_TEMPLATE, e=res, template_settings=dict(name='__ERROR_PAGE_TEMPLATE'))) + + def _handle(self, environ): + path = environ['bottle.raw_path'] = environ['PATH_INFO'] + if py3k: + environ['PATH_INFO'] = path.encode('latin1').decode('utf8', 'ignore') + + environ['bottle.app'] = self + request.bind(environ) + response.bind() + + try: + while True: # Remove in 0.14 together with RouteReset + out = None + try: + self.trigger_hook('before_request') + route, args = self.router.match(environ) + environ['route.handle'] = route + environ['bottle.route'] = route + environ['route.url_args'] = args + out = route.call(**args) + break + except HTTPResponse as E: + out = E + break + except RouteReset: + depr(0, 13, "RouteReset exception deprecated", + "Call route.call() after route.reset() and " + "return the result.") + route.reset() + continue + finally: + if isinstance(out, HTTPResponse): + out.apply(response) + try: + self.trigger_hook('after_request') + except HTTPResponse as E: + out = E + out.apply(response) + except (KeyboardInterrupt, SystemExit, MemoryError): + raise + except Exception as E: + if not self.catchall: raise + stacktrace = format_exc() + environ['wsgi.errors'].write(stacktrace) + environ['wsgi.errors'].flush() + environ['bottle.exc_info'] = sys.exc_info() + out = HTTPError(500, "Internal Server Error", E, stacktrace) + out.apply(response) + + return out + + def _cast(self, out, peek=None): + """ Try to convert the parameter into something WSGI compatible and set + correct HTTP headers when possible. + Support: False, str, unicode, dict, HTTPResponse, HTTPError, file-like, + iterable of strings and iterable of unicodes + """ + + # Empty output is done here + if not out: + if 'Content-Length' not in response: + response['Content-Length'] = 0 + return [] + # Join lists of byte or unicode strings. Mixed lists are NOT supported + if isinstance(out, (tuple, list))\ + and isinstance(out[0], (bytes, unicode)): + out = out[0][0:0].join(out) # b'abc'[0:0] -> b'' + # Encode unicode strings + if isinstance(out, unicode): + out = out.encode(response.charset) + # Byte Strings are just returned + if isinstance(out, bytes): + if 'Content-Length' not in response: + response['Content-Length'] = len(out) + return [out] + # HTTPError or HTTPException (recursive, because they may wrap anything) + # TODO: Handle these explicitly in handle() or make them iterable. + if isinstance(out, HTTPError): + out.apply(response) + out = self.error_handler.get(out.status_code, + self.default_error_handler)(out) + return self._cast(out) + if isinstance(out, HTTPResponse): + out.apply(response) + return self._cast(out.body) + + # File-like objects. + if hasattr(out, 'read'): + if 'wsgi.file_wrapper' in request.environ: + return request.environ['wsgi.file_wrapper'](out) + elif hasattr(out, 'close') or not hasattr(out, '__iter__'): + return WSGIFileWrapper(out) + + # Handle Iterables. We peek into them to detect their inner type. + try: + iout = iter(out) + first = next(iout) + while not first: + first = next(iout) + except StopIteration: + return self._cast('') + except HTTPResponse as E: + first = E + except (KeyboardInterrupt, SystemExit, MemoryError): + raise + except Exception as error: + if not self.catchall: raise + first = HTTPError(500, 'Unhandled exception', error, format_exc()) + + # These are the inner types allowed in iterator or generator objects. + if isinstance(first, HTTPResponse): + return self._cast(first) + elif isinstance(first, bytes): + new_iter = itertools.chain([first], iout) + elif isinstance(first, unicode): + encoder = lambda x: x.encode(response.charset) + new_iter = imap(encoder, itertools.chain([first], iout)) + else: + msg = 'Unsupported response type: %s' % type(first) + return self._cast(HTTPError(500, msg)) + if hasattr(out, 'close'): + new_iter = _closeiter(new_iter, out.close) + return new_iter + + def wsgi(self, environ, start_response): + """ The bottle WSGI-interface. """ + try: + out = self._cast(self._handle(environ)) + # rfc2616 section 4.3 + if response._status_code in (100, 101, 204, 304)\ + or environ['REQUEST_METHOD'] == 'HEAD': + if hasattr(out, 'close'): out.close() + out = [] + exc_info = environ.get('bottle.exc_info') + if exc_info is not None: + del environ['bottle.exc_info'] + start_response(response._wsgi_status_line(), response.headerlist, exc_info) + return out + except (KeyboardInterrupt, SystemExit, MemoryError): + raise + except Exception as E: + if not self.catchall: raise + err = '

Critical error while processing request: %s

' \ + % html_escape(environ.get('PATH_INFO', '/')) + if DEBUG: + err += '

Error:

\n
\n%s\n
\n' \ + '

Traceback:

\n
\n%s\n
\n' \ + % (html_escape(repr(E)), html_escape(format_exc())) + environ['wsgi.errors'].write(err) + environ['wsgi.errors'].flush() + headers = [('Content-Type', 'text/html; charset=UTF-8')] + start_response('500 INTERNAL SERVER ERROR', headers, sys.exc_info()) + return [tob(err)] + + def __call__(self, environ, start_response): + """ Each instance of :class:'Bottle' is a WSGI application. """ + return self.wsgi(environ, start_response) + + def __enter__(self): + """ Use this application as default for all module-level shortcuts. """ + default_app.push(self) + return self + + def __exit__(self, exc_type, exc_value, traceback): + default_app.pop() + + def __setattr__(self, name, value): + if name in self.__dict__: + raise AttributeError("Attribute %s already defined. Plugin conflict?" % name) + self.__dict__[name] = value + + +############################################################################### +# HTTP and WSGI Tools ########################################################## +############################################################################### + + +class BaseRequest(object): + """ A wrapper for WSGI environment dictionaries that adds a lot of + convenient access methods and properties. Most of them are read-only. + + Adding new attributes to a request actually adds them to the environ + dictionary (as 'bottle.request.ext.'). This is the recommended + way to store and access request-specific data. + """ + + __slots__ = ('environ', ) + + #: Maximum size of memory buffer for :attr:`body` in bytes. + MEMFILE_MAX = 102400 + + def __init__(self, environ=None): + """ Wrap a WSGI environ dictionary. """ + #: The wrapped WSGI environ dictionary. This is the only real attribute. + #: All other attributes actually are read-only properties. + self.environ = {} if environ is None else environ + self.environ['bottle.request'] = self + + @DictProperty('environ', 'bottle.app', read_only=True) + def app(self): + """ Bottle application handling this request. """ + raise RuntimeError('This request is not connected to an application.') + + @DictProperty('environ', 'bottle.route', read_only=True) + def route(self): + """ The bottle :class:`Route` object that matches this request. """ + raise RuntimeError('This request is not connected to a route.') + + @DictProperty('environ', 'route.url_args', read_only=True) + def url_args(self): + """ The arguments extracted from the URL. """ + raise RuntimeError('This request is not connected to a route.') + + @property + def path(self): + """ The value of ``PATH_INFO`` with exactly one prefixed slash (to fix + broken clients and avoid the "empty path" edge case). """ + return '/' + self.environ.get('PATH_INFO', '').lstrip('/') + + @property + def method(self): + """ The ``REQUEST_METHOD`` value as an uppercase string. """ + return self.environ.get('REQUEST_METHOD', 'GET').upper() + + @DictProperty('environ', 'bottle.request.headers', read_only=True) + def headers(self): + """ A :class:`WSGIHeaderDict` that provides case-insensitive access to + HTTP request headers. """ + return WSGIHeaderDict(self.environ) + + def get_header(self, name, default=None): + """ Return the value of a request header, or a given default value. """ + return self.headers.get(name, default) + + @DictProperty('environ', 'bottle.request.cookies', read_only=True) + def cookies(self): + """ Cookies parsed into a :class:`FormsDict`. Signed cookies are NOT + decoded. Use :meth:`get_cookie` if you expect signed cookies. """ + cookies = SimpleCookie(self.environ.get('HTTP_COOKIE', '')).values() + return FormsDict((c.key, c.value) for c in cookies) + + def get_cookie(self, key, default=None, secret=None, digestmod=hashlib.sha256): + """ Return the content of a cookie. To read a `Signed Cookie`, the + `secret` must match the one used to create the cookie (see + :meth:`BaseResponse.set_cookie`). If anything goes wrong (missing + cookie or wrong signature), return a default value. """ + value = self.cookies.get(key) + if secret: + # See BaseResponse.set_cookie for details on signed cookies. + if value and value.startswith('!') and '?' in value: + sig, msg = map(tob, value[1:].split('?', 1)) + hash = hmac.new(tob(secret), msg, digestmod=digestmod).digest() + if _lscmp(sig, base64.b64encode(hash)): + dst = pickle.loads(base64.b64decode(msg)) + if dst and dst[0] == key: + return dst[1] + return default + return value or default + + @DictProperty('environ', 'bottle.request.query', read_only=True) + def query(self): + """ The :attr:`query_string` parsed into a :class:`FormsDict`. These + values are sometimes called "URL arguments" or "GET parameters", but + not to be confused with "URL wildcards" as they are provided by the + :class:`Router`. """ + get = self.environ['bottle.get'] = FormsDict() + pairs = _parse_qsl(self.environ.get('QUERY_STRING', '')) + for key, value in pairs: + get[key] = value + return get + + @DictProperty('environ', 'bottle.request.forms', read_only=True) + def forms(self): + """ Form values parsed from an `url-encoded` or `multipart/form-data` + encoded POST or PUT request body. The result is returned as a + :class:`FormsDict`. All keys and values are strings. File uploads + are stored separately in :attr:`files`. """ + forms = FormsDict() + forms.recode_unicode = self.POST.recode_unicode + for name, item in self.POST.allitems(): + if not isinstance(item, FileUpload): + forms[name] = item + return forms + + @DictProperty('environ', 'bottle.request.params', read_only=True) + def params(self): + """ A :class:`FormsDict` with the combined values of :attr:`query` and + :attr:`forms`. File uploads are stored in :attr:`files`. """ + params = FormsDict() + for key, value in self.query.allitems(): + params[key] = value + for key, value in self.forms.allitems(): + params[key] = value + return params + + @DictProperty('environ', 'bottle.request.files', read_only=True) + def files(self): + """ File uploads parsed from `multipart/form-data` encoded POST or PUT + request body. The values are instances of :class:`FileUpload`. + + """ + files = FormsDict() + files.recode_unicode = self.POST.recode_unicode + for name, item in self.POST.allitems(): + if isinstance(item, FileUpload): + files[name] = item + return files + + @DictProperty('environ', 'bottle.request.json', read_only=True) + def json(self): + """ If the ``Content-Type`` header is ``application/json`` or + ``application/json-rpc``, this property holds the parsed content + of the request body. Only requests smaller than :attr:`MEMFILE_MAX` + are processed to avoid memory exhaustion. + Invalid JSON raises a 400 error response. + """ + ctype = self.environ.get('CONTENT_TYPE', '').lower().split(';')[0] + if ctype in ('application/json', 'application/json-rpc'): + b = self._get_body_string(self.MEMFILE_MAX) + if not b: + return None + try: + return json_loads(b) + except (ValueError, TypeError): + raise HTTPError(400, 'Invalid JSON') + return None + + def _iter_body(self, read, bufsize): + maxread = max(0, self.content_length) + while maxread: + part = read(min(maxread, bufsize)) + if not part: break + yield part + maxread -= len(part) + + @staticmethod + def _iter_chunked(read, bufsize): + err = HTTPError(400, 'Error while parsing chunked transfer body.') + rn, sem, bs = tob('\r\n'), tob(';'), tob('') + while True: + header = read(1) + while header[-2:] != rn: + c = read(1) + header += c + if not c: raise err + if len(header) > bufsize: raise err + size, _, _ = header.partition(sem) + try: + maxread = int(tonat(size.strip()), 16) + except ValueError: + raise err + if maxread == 0: break + buff = bs + while maxread > 0: + if not buff: + buff = read(min(maxread, bufsize)) + part, buff = buff[:maxread], buff[maxread:] + if not part: raise err + yield part + maxread -= len(part) + if read(2) != rn: + raise err + + @DictProperty('environ', 'bottle.request.body', read_only=True) + def _body(self): + try: + read_func = self.environ['wsgi.input'].read + except KeyError: + self.environ['wsgi.input'] = BytesIO() + return self.environ['wsgi.input'] + body_iter = self._iter_chunked if self.chunked else self._iter_body + body, body_size, is_temp_file = BytesIO(), 0, False + for part in body_iter(read_func, self.MEMFILE_MAX): + body.write(part) + body_size += len(part) + if not is_temp_file and body_size > self.MEMFILE_MAX: + body, tmp = NamedTemporaryFile(mode='w+b'), body + body.write(tmp.getvalue()) + del tmp + is_temp_file = True + self.environ['wsgi.input'] = body + body.seek(0) + return body + + def _get_body_string(self, maxread): + """ Read body into a string. Raise HTTPError(413) on requests that are + too large. """ + if self.content_length > maxread: + raise HTTPError(413, 'Request entity too large') + data = self.body.read(maxread + 1) + if len(data) > maxread: + raise HTTPError(413, 'Request entity too large') + return data + + @property + def body(self): + """ The HTTP request body as a seek-able file-like object. Depending on + :attr:`MEMFILE_MAX`, this is either a temporary file or a + :class:`io.BytesIO` instance. Accessing this property for the first + time reads and replaces the ``wsgi.input`` environ variable. + Subsequent accesses just do a `seek(0)` on the file object. """ + self._body.seek(0) + return self._body + + @property + def chunked(self): + """ True if Chunked transfer encoding was. """ + return 'chunked' in self.environ.get( + 'HTTP_TRANSFER_ENCODING', '').lower() + + #: An alias for :attr:`query`. + GET = query + + @DictProperty('environ', 'bottle.request.post', read_only=True) + def POST(self): + """ The values of :attr:`forms` and :attr:`files` combined into a single + :class:`FormsDict`. Values are either strings (form values) or + instances of :class:`cgi.FieldStorage` (file uploads). + """ + post = FormsDict() + # We default to application/x-www-form-urlencoded for everything that + # is not multipart and take the fast path (also: 3.1 workaround) + if not self.content_type.startswith('multipart/'): + body = tonat(self._get_body_string(self.MEMFILE_MAX), 'latin1') + for key, value in _parse_qsl(body): + post[key] = value + return post + + safe_env = {'QUERY_STRING': ''} # Build a safe environment for cgi + for key in ('REQUEST_METHOD', 'CONTENT_TYPE', 'CONTENT_LENGTH'): + if key in self.environ: safe_env[key] = self.environ[key] + args = dict(fp=self.body, environ=safe_env, keep_blank_values=True) + + if py3k: + args['encoding'] = 'utf8' + post.recode_unicode = False + data = cgi.FieldStorage(**args) + self['_cgi.FieldStorage'] = data #http://bugs.python.org/issue18394 + data = data.list or [] + for item in data: + if item.filename is None: + post[item.name] = item.value + else: + post[item.name] = FileUpload(item.file, item.name, + item.filename, item.headers) + return post + + @property + def url(self): + """ The full request URI including hostname and scheme. If your app + lives behind a reverse proxy or load balancer and you get confusing + results, make sure that the ``X-Forwarded-Host`` header is set + correctly. """ + return self.urlparts.geturl() + + @DictProperty('environ', 'bottle.request.urlparts', read_only=True) + def urlparts(self): + """ The :attr:`url` string as an :class:`urlparse.SplitResult` tuple. + The tuple contains (scheme, host, path, query_string and fragment), + but the fragment is always empty because it is not visible to the + server. """ + env = self.environ + http = env.get('HTTP_X_FORWARDED_PROTO') \ + or env.get('wsgi.url_scheme', 'http') + host = env.get('HTTP_X_FORWARDED_HOST') or env.get('HTTP_HOST') + if not host: + # HTTP 1.1 requires a Host-header. This is for HTTP/1.0 clients. + host = env.get('SERVER_NAME', '127.0.0.1') + port = env.get('SERVER_PORT') + if port and port != ('80' if http == 'http' else '443'): + host += ':' + port + path = urlquote(self.fullpath) + return UrlSplitResult(http, host, path, env.get('QUERY_STRING'), '') + + @property + def fullpath(self): + """ Request path including :attr:`script_name` (if present). """ + return urljoin(self.script_name, self.path.lstrip('/')) + + @property + def query_string(self): + """ The raw :attr:`query` part of the URL (everything in between ``?`` + and ``#``) as a string. """ + return self.environ.get('QUERY_STRING', '') + + @property + def script_name(self): + """ The initial portion of the URL's `path` that was removed by a higher + level (server or routing middleware) before the application was + called. This script path is returned with leading and tailing + slashes. """ + script_name = self.environ.get('SCRIPT_NAME', '').strip('/') + return '/' + script_name + '/' if script_name else '/' + + def path_shift(self, shift=1): + """ Shift path segments from :attr:`path` to :attr:`script_name` and + vice versa. + + :param shift: The number of path segments to shift. May be negative + to change the shift direction. (default: 1) + """ + script, path = path_shift(self.environ.get('SCRIPT_NAME', '/'), self.path, shift) + self['SCRIPT_NAME'], self['PATH_INFO'] = script, path + + @property + def content_length(self): + """ The request body length as an integer. The client is responsible to + set this header. Otherwise, the real length of the body is unknown + and -1 is returned. In this case, :attr:`body` will be empty. """ + return int(self.environ.get('CONTENT_LENGTH') or -1) + + @property + def content_type(self): + """ The Content-Type header as a lowercase-string (default: empty). """ + return self.environ.get('CONTENT_TYPE', '').lower() + + @property + def is_xhr(self): + """ True if the request was triggered by a XMLHttpRequest. This only + works with JavaScript libraries that support the `X-Requested-With` + header (most of the popular libraries do). """ + requested_with = self.environ.get('HTTP_X_REQUESTED_WITH', '') + return requested_with.lower() == 'xmlhttprequest' + + @property + def is_ajax(self): + """ Alias for :attr:`is_xhr`. "Ajax" is not the right term. """ + return self.is_xhr + + @property + def auth(self): + """ HTTP authentication data as a (user, password) tuple. This + implementation currently supports basic (not digest) authentication + only. If the authentication happened at a higher level (e.g. in the + front web-server or a middleware), the password field is None, but + the user field is looked up from the ``REMOTE_USER`` environ + variable. On any errors, None is returned. """ + basic = parse_auth(self.environ.get('HTTP_AUTHORIZATION', '')) + if basic: return basic + ruser = self.environ.get('REMOTE_USER') + if ruser: return (ruser, None) + return None + + @property + def remote_route(self): + """ A list of all IPs that were involved in this request, starting with + the client IP and followed by zero or more proxies. This does only + work if all proxies support the ```X-Forwarded-For`` header. Note + that this information can be forged by malicious clients. """ + proxy = self.environ.get('HTTP_X_FORWARDED_FOR') + if proxy: return [ip.strip() for ip in proxy.split(',')] + remote = self.environ.get('REMOTE_ADDR') + return [remote] if remote else [] + + @property + def remote_addr(self): + """ The client IP as a string. Note that this information can be forged + by malicious clients. """ + route = self.remote_route + return route[0] if route else None + + def copy(self): + """ Return a new :class:`Request` with a shallow :attr:`environ` copy. """ + return Request(self.environ.copy()) + + def get(self, value, default=None): + return self.environ.get(value, default) + + def __getitem__(self, key): + return self.environ[key] + + def __delitem__(self, key): + self[key] = "" + del (self.environ[key]) + + def __iter__(self): + return iter(self.environ) + + def __len__(self): + return len(self.environ) + + def keys(self): + return self.environ.keys() + + def __setitem__(self, key, value): + """ Change an environ value and clear all caches that depend on it. """ + + if self.environ.get('bottle.request.readonly'): + raise KeyError('The environ dictionary is read-only.') + + self.environ[key] = value + todelete = () + + if key == 'wsgi.input': + todelete = ('body', 'forms', 'files', 'params', 'post', 'json') + elif key == 'QUERY_STRING': + todelete = ('query', 'params') + elif key.startswith('HTTP_'): + todelete = ('headers', 'cookies') + + for key in todelete: + self.environ.pop('bottle.request.' + key, None) + + def __repr__(self): + return '<%s: %s %s>' % (self.__class__.__name__, self.method, self.url) + + def __getattr__(self, name): + """ Search in self.environ for additional user defined attributes. """ + try: + var = self.environ['bottle.request.ext.%s' % name] + return var.__get__(self) if hasattr(var, '__get__') else var + except KeyError: + raise AttributeError('Attribute %r not defined.' % name) + + def __setattr__(self, name, value): + if name == 'environ': return object.__setattr__(self, name, value) + key = 'bottle.request.ext.%s' % name + if hasattr(self, name): + raise AttributeError("Attribute already defined: %s" % name) + self.environ[key] = value + + def __delattr__(self, name): + try: + del self.environ['bottle.request.ext.%s' % name] + except KeyError: + raise AttributeError("Attribute not defined: %s" % name) + + +def _hkey(key): + if '\n' in key or '\r' in key or '\0' in key: + raise ValueError("Header names must not contain control characters: %r" % key) + return key.title().replace('_', '-') + + +def _hval(value): + value = tonat(value) + if '\n' in value or '\r' in value or '\0' in value: + raise ValueError("Header value must not contain control characters: %r" % value) + return value + + +class HeaderProperty(object): + def __init__(self, name, reader=None, writer=None, default=''): + self.name, self.default = name, default + self.reader, self.writer = reader, writer + self.__doc__ = 'Current value of the %r header.' % name.title() + + def __get__(self, obj, _): + if obj is None: return self + value = obj.get_header(self.name, self.default) + return self.reader(value) if self.reader else value + + def __set__(self, obj, value): + obj[self.name] = self.writer(value) if self.writer else value + + def __delete__(self, obj): + del obj[self.name] + + +class BaseResponse(object): + """ Storage class for a response body as well as headers and cookies. + + This class does support dict-like case-insensitive item-access to + headers, but is NOT a dict. Most notably, iterating over a response + yields parts of the body and not the headers. + + :param body: The response body as one of the supported types. + :param status: Either an HTTP status code (e.g. 200) or a status line + including the reason phrase (e.g. '200 OK'). + :param headers: A dictionary or a list of name-value pairs. + + Additional keyword arguments are added to the list of headers. + Underscores in the header name are replaced with dashes. + """ + + default_status = 200 + default_content_type = 'text/html; charset=UTF-8' + + # Header denylist for specific response codes + # (rfc2616 section 10.2.3 and 10.3.5) + bad_headers = { + 204: frozenset(('Content-Type', 'Content-Length')), + 304: frozenset(('Allow', 'Content-Encoding', 'Content-Language', + 'Content-Length', 'Content-Range', 'Content-Type', + 'Content-Md5', 'Last-Modified')) + } + + def __init__(self, body='', status=None, headers=None, **more_headers): + self._cookies = None + self._headers = {} + self.body = body + self.status = status or self.default_status + if headers: + if isinstance(headers, dict): + headers = headers.items() + for name, value in headers: + self.add_header(name, value) + if more_headers: + for name, value in more_headers.items(): + self.add_header(name, value) + + def copy(self, cls=None): + """ Returns a copy of self. """ + cls = cls or BaseResponse + assert issubclass(cls, BaseResponse) + copy = cls() + copy.status = self.status + copy._headers = dict((k, v[:]) for (k, v) in self._headers.items()) + if self._cookies: + cookies = copy._cookies = SimpleCookie() + for k,v in self._cookies.items(): + cookies[k] = v.value + cookies[k].update(v) # also copy cookie attributes + return copy + + def __iter__(self): + return iter(self.body) + + def close(self): + if hasattr(self.body, 'close'): + self.body.close() + + @property + def status_line(self): + """ The HTTP status line as a string (e.g. ``404 Not Found``).""" + return self._status_line + + @property + def status_code(self): + """ The HTTP status code as an integer (e.g. 404).""" + return self._status_code + + def _set_status(self, status): + if isinstance(status, int): + code, status = status, _HTTP_STATUS_LINES.get(status) + elif ' ' in status: + if '\n' in status or '\r' in status or '\0' in status: + raise ValueError('Status line must not include control chars.') + status = status.strip() + code = int(status.split()[0]) + else: + raise ValueError('String status line without a reason phrase.') + if not 100 <= code <= 999: + raise ValueError('Status code out of range.') + self._status_code = code + self._status_line = str(status or ('%d Unknown' % code)) + + def _get_status(self): + return self._status_line + + status = property( + _get_status, _set_status, None, + ''' A writeable property to change the HTTP response status. It accepts + either a numeric code (100-999) or a string with a custom reason + phrase (e.g. "404 Brain not found"). Both :data:`status_line` and + :data:`status_code` are updated accordingly. The return value is + always a status string. ''') + del _get_status, _set_status + + @property + def headers(self): + """ An instance of :class:`HeaderDict`, a case-insensitive dict-like + view on the response headers. """ + hdict = HeaderDict() + hdict.dict = self._headers + return hdict + + def __contains__(self, name): + return _hkey(name) in self._headers + + def __delitem__(self, name): + del self._headers[_hkey(name)] + + def __getitem__(self, name): + return self._headers[_hkey(name)][-1] + + def __setitem__(self, name, value): + self._headers[_hkey(name)] = [_hval(value)] + + def get_header(self, name, default=None): + """ Return the value of a previously defined header. If there is no + header with that name, return a default value. """ + return self._headers.get(_hkey(name), [default])[-1] + + def set_header(self, name, value): + """ Create a new response header, replacing any previously defined + headers with the same name. """ + self._headers[_hkey(name)] = [_hval(value)] + + def add_header(self, name, value): + """ Add an additional response header, not removing duplicates. """ + self._headers.setdefault(_hkey(name), []).append(_hval(value)) + + def iter_headers(self): + """ Yield (header, value) tuples, skipping headers that are not + allowed with the current response status code. """ + return self.headerlist + + def _wsgi_status_line(self): + """ WSGI conform status line (latin1-encodeable) """ + if py3k: + return self._status_line.encode('utf8').decode('latin1') + return self._status_line + + @property + def headerlist(self): + """ WSGI conform list of (header, value) tuples. """ + out = [] + headers = list(self._headers.items()) + if 'Content-Type' not in self._headers: + headers.append(('Content-Type', [self.default_content_type])) + if self._status_code in self.bad_headers: + bad_headers = self.bad_headers[self._status_code] + headers = [h for h in headers if h[0] not in bad_headers] + out += [(name, val) for (name, vals) in headers for val in vals] + if self._cookies: + for c in self._cookies.values(): + out.append(('Set-Cookie', _hval(c.OutputString()))) + if py3k: + out = [(k, v.encode('utf8').decode('latin1')) for (k, v) in out] + return out + + content_type = HeaderProperty('Content-Type') + content_length = HeaderProperty('Content-Length', reader=int, default=-1) + expires = HeaderProperty( + 'Expires', + reader=lambda x: datetime.utcfromtimestamp(parse_date(x)), + writer=lambda x: http_date(x)) + + @property + def charset(self, default='UTF-8'): + """ Return the charset specified in the content-type header (default: utf8). """ + if 'charset=' in self.content_type: + return self.content_type.split('charset=')[-1].split(';')[0].strip() + return default + + def set_cookie(self, name, value, secret=None, digestmod=hashlib.sha256, **options): + """ Create a new cookie or replace an old one. If the `secret` parameter is + set, create a `Signed Cookie` (described below). + + :param name: the name of the cookie. + :param value: the value of the cookie. + :param secret: a signature key required for signed cookies. + + Additionally, this method accepts all RFC 2109 attributes that are + supported by :class:`cookie.Morsel`, including: + + :param maxage: maximum age in seconds. (default: None) + :param expires: a datetime object or UNIX timestamp. (default: None) + :param domain: the domain that is allowed to read the cookie. + (default: current domain) + :param path: limits the cookie to a given path (default: current path) + :param secure: limit the cookie to HTTPS connections (default: off). + :param httponly: prevents client-side javascript to read this cookie + (default: off, requires Python 2.6 or newer). + :param samesite: Control or disable third-party use for this cookie. + Possible values: `lax`, `strict` or `none` (default). + + If neither `expires` nor `maxage` is set (default), the cookie will + expire at the end of the browser session (as soon as the browser + window is closed). + + Signed cookies may store any pickle-able object and are + cryptographically signed to prevent manipulation. Keep in mind that + cookies are limited to 4kb in most browsers. + + Warning: Pickle is a potentially dangerous format. If an attacker + gains access to the secret key, he could forge cookies that execute + code on server side if unpickled. Using pickle is discouraged and + support for it will be removed in later versions of bottle. + + Warning: Signed cookies are not encrypted (the client can still see + the content) and not copy-protected (the client can restore an old + cookie). The main intention is to make pickling and unpickling + save, not to store secret information at client side. + """ + if not self._cookies: + self._cookies = SimpleCookie() + + # Monkey-patch Cookie lib to support 'SameSite' parameter + # https://tools.ietf.org/html/draft-west-first-party-cookies-07#section-4.1 + if py < (3, 8, 0): + Morsel._reserved.setdefault('samesite', 'SameSite') + + if secret: + if not isinstance(value, basestring): + depr(0, 13, "Pickling of arbitrary objects into cookies is " + "deprecated.", "Only store strings in cookies. " + "JSON strings are fine, too.") + encoded = base64.b64encode(pickle.dumps([name, value], -1)) + sig = base64.b64encode(hmac.new(tob(secret), encoded, + digestmod=digestmod).digest()) + value = touni(tob('!') + sig + tob('?') + encoded) + elif not isinstance(value, basestring): + raise TypeError('Secret key required for non-string cookies.') + + # Cookie size plus options must not exceed 4kb. + if len(name) + len(value) > 3800: + raise ValueError('Content does not fit into a cookie.') + + self._cookies[name] = value + + for key, value in options.items(): + if key in ('max_age', 'maxage'): # 'maxage' variant added in 0.13 + key = 'max-age' + if isinstance(value, timedelta): + value = value.seconds + value.days * 24 * 3600 + if key == 'expires': + value = http_date(value) + if key in ('same_site', 'samesite'): # 'samesite' variant added in 0.13 + key, value = 'samesite', (value or "none").lower() + if value not in ('lax', 'strict', 'none'): + raise CookieError("Invalid value for SameSite") + if key in ('secure', 'httponly') and not value: + continue + self._cookies[name][key] = value + + def delete_cookie(self, key, **kwargs): + """ Delete a cookie. Be sure to use the same `domain` and `path` + settings as used to create the cookie. """ + kwargs['max_age'] = -1 + kwargs['expires'] = 0 + self.set_cookie(key, '', **kwargs) + + def __repr__(self): + out = '' + for name, value in self.headerlist: + out += '%s: %s\n' % (name.title(), value.strip()) + return out + + +def _local_property(): + ls = threading.local() + + def fget(_): + try: + return ls.var + except AttributeError: + raise RuntimeError("Request context not initialized.") + + def fset(_, value): + ls.var = value + + def fdel(_): + del ls.var + + return property(fget, fset, fdel, 'Thread-local property') + + +class LocalRequest(BaseRequest): + """ A thread-local subclass of :class:`BaseRequest` with a different + set of attributes for each thread. There is usually only one global + instance of this class (:data:`request`). If accessed during a + request/response cycle, this instance always refers to the *current* + request (even on a multithreaded server). """ + bind = BaseRequest.__init__ + environ = _local_property() + + +class LocalResponse(BaseResponse): + """ A thread-local subclass of :class:`BaseResponse` with a different + set of attributes for each thread. There is usually only one global + instance of this class (:data:`response`). Its attributes are used + to build the HTTP response at the end of the request/response cycle. + """ + bind = BaseResponse.__init__ + _status_line = _local_property() + _status_code = _local_property() + _cookies = _local_property() + _headers = _local_property() + body = _local_property() + + +Request = BaseRequest +Response = BaseResponse + + +class HTTPResponse(Response, BottleException): + def __init__(self, body='', status=None, headers=None, **more_headers): + super(HTTPResponse, self).__init__(body, status, headers, **more_headers) + + def apply(self, other): + other._status_code = self._status_code + other._status_line = self._status_line + other._headers = self._headers + other._cookies = self._cookies + other.body = self.body + + +class HTTPError(HTTPResponse): + default_status = 500 + + def __init__(self, + status=None, + body=None, + exception=None, + traceback=None, **more_headers): + self.exception = exception + self.traceback = traceback + super(HTTPError, self).__init__(body, status, **more_headers) + +############################################################################### +# Plugins ###################################################################### +############################################################################### + + +class PluginError(BottleException): + pass + + +class JSONPlugin(object): + name = 'json' + api = 2 + + def __init__(self, json_dumps=json_dumps): + self.json_dumps = json_dumps + + def setup(self, app): + app.config._define('json.enable', default=True, validate=bool, + help="Enable or disable automatic dict->json filter.") + app.config._define('json.ascii', default=False, validate=bool, + help="Use only 7-bit ASCII characters in output.") + app.config._define('json.indent', default=True, validate=bool, + help="Add whitespace to make json more readable.") + app.config._define('json.dump_func', default=None, + help="If defined, use this function to transform" + " dict into json. The other options no longer" + " apply.") + + def apply(self, callback, route): + dumps = self.json_dumps + if not self.json_dumps: return callback + + @functools.wraps(callback) + def wrapper(*a, **ka): + try: + rv = callback(*a, **ka) + except HTTPResponse as resp: + rv = resp + + if isinstance(rv, dict): + #Attempt to serialize, raises exception on failure + json_response = dumps(rv) + #Set content type only if serialization successful + response.content_type = 'application/json' + return json_response + elif isinstance(rv, HTTPResponse) and isinstance(rv.body, dict): + rv.body = dumps(rv.body) + rv.content_type = 'application/json' + return rv + + return wrapper + + +class TemplatePlugin(object): + """ This plugin applies the :func:`view` decorator to all routes with a + `template` config parameter. If the parameter is a tuple, the second + element must be a dict with additional options (e.g. `template_engine`) + or default variables for the template. """ + name = 'template' + api = 2 + + def setup(self, app): + app.tpl = self + + def apply(self, callback, route): + conf = route.config.get('template') + if isinstance(conf, (tuple, list)) and len(conf) == 2: + return view(conf[0], **conf[1])(callback) + elif isinstance(conf, str): + return view(conf)(callback) + else: + return callback + + +#: Not a plugin, but part of the plugin API. TODO: Find a better place. +class _ImportRedirect(object): + def __init__(self, name, impmask): + """ Create a virtual package that redirects imports (see PEP 302). """ + self.name = name + self.impmask = impmask + self.module = sys.modules.setdefault(name, new_module(name)) + self.module.__dict__.update({ + '__file__': __file__, + '__path__': [], + '__all__': [], + '__loader__': self + }) + sys.meta_path.append(self) + + def find_spec(self, fullname, path, target=None): + if '.' not in fullname: return + if fullname.rsplit('.', 1)[0] != self.name: return + from importlib.util import spec_from_loader + return spec_from_loader(fullname, self) + + def find_module(self, fullname, path=None): + if '.' not in fullname: return + if fullname.rsplit('.', 1)[0] != self.name: return + return self + + def load_module(self, fullname): + if fullname in sys.modules: return sys.modules[fullname] + modname = fullname.rsplit('.', 1)[1] + realname = self.impmask % modname + __import__(realname) + module = sys.modules[fullname] = sys.modules[realname] + setattr(self.module, modname, module) + module.__loader__ = self + return module + +############################################################################### +# Common Utilities ############################################################# +############################################################################### + + +class MultiDict(DictMixin): + """ This dict stores multiple values per key, but behaves exactly like a + normal dict in that it returns only the newest value for any given key. + There are special methods available to access the full list of values. + """ + + def __init__(self, *a, **k): + self.dict = dict((k, [v]) for (k, v) in dict(*a, **k).items()) + + def __len__(self): + return len(self.dict) + + def __iter__(self): + return iter(self.dict) + + def __contains__(self, key): + return key in self.dict + + def __delitem__(self, key): + del self.dict[key] + + def __getitem__(self, key): + return self.dict[key][-1] + + def __setitem__(self, key, value): + self.append(key, value) + + def keys(self): + return self.dict.keys() + + if py3k: + + def values(self): + return (v[-1] for v in self.dict.values()) + + def items(self): + return ((k, v[-1]) for k, v in self.dict.items()) + + def allitems(self): + return ((k, v) for k, vl in self.dict.items() for v in vl) + + iterkeys = keys + itervalues = values + iteritems = items + iterallitems = allitems + + else: + + def values(self): + return [v[-1] for v in self.dict.values()] + + def items(self): + return [(k, v[-1]) for k, v in self.dict.items()] + + def iterkeys(self): + return self.dict.iterkeys() + + def itervalues(self): + return (v[-1] for v in self.dict.itervalues()) + + def iteritems(self): + return ((k, v[-1]) for k, v in self.dict.iteritems()) + + def iterallitems(self): + return ((k, v) for k, vl in self.dict.iteritems() for v in vl) + + def allitems(self): + return [(k, v) for k, vl in self.dict.iteritems() for v in vl] + + def get(self, key, default=None, index=-1, type=None): + """ Return the most recent value for a key. + + :param default: The default value to be returned if the key is not + present or the type conversion fails. + :param index: An index for the list of available values. + :param type: If defined, this callable is used to cast the value + into a specific type. Exception are suppressed and result in + the default value to be returned. + """ + try: + val = self.dict[key][index] + return type(val) if type else val + except Exception: + pass + return default + + def append(self, key, value): + """ Add a new value to the list of values for this key. """ + self.dict.setdefault(key, []).append(value) + + def replace(self, key, value): + """ Replace the list of values with a single value. """ + self.dict[key] = [value] + + def getall(self, key): + """ Return a (possibly empty) list of values for a key. """ + return self.dict.get(key) or [] + + #: Aliases for WTForms to mimic other multi-dict APIs (Django) + getone = get + getlist = getall + + +class FormsDict(MultiDict): + """ This :class:`MultiDict` subclass is used to store request form data. + Additionally to the normal dict-like item access methods (which return + unmodified data as native strings), this container also supports + attribute-like access to its values. Attributes are automatically de- + or recoded to match :attr:`input_encoding` (default: 'utf8'). Missing + attributes default to an empty string. """ + + #: Encoding used for attribute values. + input_encoding = 'utf8' + #: If true (default), unicode strings are first encoded with `latin1` + #: and then decoded to match :attr:`input_encoding`. + recode_unicode = True + + def _fix(self, s, encoding=None): + if isinstance(s, unicode) and self.recode_unicode: # Python 3 WSGI + return s.encode('latin1').decode(encoding or self.input_encoding) + elif isinstance(s, bytes): # Python 2 WSGI + return s.decode(encoding or self.input_encoding) + else: + return s + + def decode(self, encoding=None): + """ Returns a copy with all keys and values de- or recoded to match + :attr:`input_encoding`. Some libraries (e.g. WTForms) want a + unicode dictionary. """ + copy = FormsDict() + enc = copy.input_encoding = encoding or self.input_encoding + copy.recode_unicode = False + for key, value in self.allitems(): + copy.append(self._fix(key, enc), self._fix(value, enc)) + return copy + + def getunicode(self, name, default=None, encoding=None): + """ Return the value as a unicode string, or the default. """ + try: + return self._fix(self[name], encoding) + except (UnicodeError, KeyError): + return default + + def __getattr__(self, name, default=unicode()): + # Without this guard, pickle generates a cryptic TypeError: + if name.startswith('__') and name.endswith('__'): + return super(FormsDict, self).__getattr__(name) + return self.getunicode(name, default=default) + +class HeaderDict(MultiDict): + """ A case-insensitive version of :class:`MultiDict` that defaults to + replace the old value instead of appending it. """ + + def __init__(self, *a, **ka): + self.dict = {} + if a or ka: self.update(*a, **ka) + + def __contains__(self, key): + return _hkey(key) in self.dict + + def __delitem__(self, key): + del self.dict[_hkey(key)] + + def __getitem__(self, key): + return self.dict[_hkey(key)][-1] + + def __setitem__(self, key, value): + self.dict[_hkey(key)] = [_hval(value)] + + def append(self, key, value): + self.dict.setdefault(_hkey(key), []).append(_hval(value)) + + def replace(self, key, value): + self.dict[_hkey(key)] = [_hval(value)] + + def getall(self, key): + return self.dict.get(_hkey(key)) or [] + + def get(self, key, default=None, index=-1): + return MultiDict.get(self, _hkey(key), default, index) + + def filter(self, names): + for name in (_hkey(n) for n in names): + if name in self.dict: + del self.dict[name] + + +class WSGIHeaderDict(DictMixin): + """ This dict-like class wraps a WSGI environ dict and provides convenient + access to HTTP_* fields. Keys and values are native strings + (2.x bytes or 3.x unicode) and keys are case-insensitive. If the WSGI + environment contains non-native string values, these are de- or encoded + using a lossless 'latin1' character set. + + The API will remain stable even on changes to the relevant PEPs. + Currently PEP 333, 444 and 3333 are supported. (PEP 444 is the only one + that uses non-native strings.) + """ + #: List of keys that do not have a ``HTTP_`` prefix. + cgikeys = ('CONTENT_TYPE', 'CONTENT_LENGTH') + + def __init__(self, environ): + self.environ = environ + + def _ekey(self, key): + """ Translate header field name to CGI/WSGI environ key. """ + key = key.replace('-', '_').upper() + if key in self.cgikeys: + return key + return 'HTTP_' + key + + def raw(self, key, default=None): + """ Return the header value as is (may be bytes or unicode). """ + return self.environ.get(self._ekey(key), default) + + def __getitem__(self, key): + val = self.environ[self._ekey(key)] + if py3k: + if isinstance(val, unicode): + val = val.encode('latin1').decode('utf8') + else: + val = val.decode('utf8') + return val + + def __setitem__(self, key, value): + raise TypeError("%s is read-only." % self.__class__) + + def __delitem__(self, key): + raise TypeError("%s is read-only." % self.__class__) + + def __iter__(self): + for key in self.environ: + if key[:5] == 'HTTP_': + yield _hkey(key[5:]) + elif key in self.cgikeys: + yield _hkey(key) + + def keys(self): + return [x for x in self] + + def __len__(self): + return len(self.keys()) + + def __contains__(self, key): + return self._ekey(key) in self.environ + +_UNSET = object() + +class ConfigDict(dict): + """ A dict-like configuration storage with additional support for + namespaces, validators, meta-data, overlays and more. + + This dict-like class is heavily optimized for read access. All read-only + methods as well as item access should be as fast as the built-in dict. + """ + + __slots__ = ('_meta', '_change_listener', '_overlays', '_virtual_keys', '_source', '__weakref__') + + def __init__(self): + self._meta = {} + self._change_listener = [] + #: Weak references of overlays that need to be kept in sync. + self._overlays = [] + #: Config that is the source for this overlay. + self._source = None + #: Keys of values copied from the source (values we do not own) + self._virtual_keys = set() + + def load_module(self, path, squash=True): + """Load values from a Python module. + + Example modue ``config.py``:: + + DEBUG = True + SQLITE = { + "db": ":memory:" + } + + + >>> c = ConfigDict() + >>> c.load_module('config') + {DEBUG: True, 'SQLITE.DB': 'memory'} + >>> c.load_module("config", False) + {'DEBUG': True, 'SQLITE': {'DB': 'memory'}} + + :param squash: If true (default), dictionary values are assumed to + represent namespaces (see :meth:`load_dict`). + """ + config_obj = load(path) + obj = {key: getattr(config_obj, key) for key in dir(config_obj) + if key.isupper()} + + if squash: + self.load_dict(obj) + else: + self.update(obj) + return self + + def load_config(self, filename, **options): + """ Load values from an ``*.ini`` style config file. + + A configuration file consists of sections, each led by a + ``[section]`` header, followed by key/value entries separated by + either ``=`` or ``:``. Section names and keys are case-insensitive. + Leading and trailing whitespace is removed from keys and values. + Values can be omitted, in which case the key/value delimiter may + also be left out. Values can also span multiple lines, as long as + they are indented deeper than the first line of the value. Commands + are prefixed by ``#`` or ``;`` and may only appear on their own on + an otherwise empty line. + + Both section and key names may contain dots (``.``) as namespace + separators. The actual configuration parameter name is constructed + by joining section name and key name together and converting to + lower case. + + The special sections ``bottle`` and ``ROOT`` refer to the root + namespace and the ``DEFAULT`` section defines default values for all + other sections. + + With Python 3, extended string interpolation is enabled. + + :param filename: The path of a config file, or a list of paths. + :param options: All keyword parameters are passed to the underlying + :class:`python:configparser.ConfigParser` constructor call. + + """ + options.setdefault('allow_no_value', True) + if py3k: + options.setdefault('interpolation', + configparser.ExtendedInterpolation()) + conf = configparser.ConfigParser(**options) + conf.read(filename) + for section in conf.sections(): + for key in conf.options(section): + value = conf.get(section, key) + if section not in ('bottle', 'ROOT'): + key = section + '.' + key + self[key.lower()] = value + return self + + def load_dict(self, source, namespace=''): + """ Load values from a dictionary structure. Nesting can be used to + represent namespaces. + + >>> c = ConfigDict() + >>> c.load_dict({'some': {'namespace': {'key': 'value'} } }) + {'some.namespace.key': 'value'} + """ + for key, value in source.items(): + if isinstance(key, basestring): + nskey = (namespace + '.' + key).strip('.') + if isinstance(value, dict): + self.load_dict(value, namespace=nskey) + else: + self[nskey] = value + else: + raise TypeError('Key has type %r (not a string)' % type(key)) + return self + + def update(self, *a, **ka): + """ If the first parameter is a string, all keys are prefixed with this + namespace. Apart from that it works just as the usual dict.update(). + + >>> c = ConfigDict() + >>> c.update('some.namespace', key='value') + """ + prefix = '' + if a and isinstance(a[0], basestring): + prefix = a[0].strip('.') + '.' + a = a[1:] + for key, value in dict(*a, **ka).items(): + self[prefix + key] = value + + def setdefault(self, key, value): + if key not in self: + self[key] = value + return self[key] + + def __setitem__(self, key, value): + if not isinstance(key, basestring): + raise TypeError('Key has type %r (not a string)' % type(key)) + + self._virtual_keys.discard(key) + + value = self.meta_get(key, 'filter', lambda x: x)(value) + if key in self and self[key] is value: + return + + self._on_change(key, value) + dict.__setitem__(self, key, value) + + for overlay in self._iter_overlays(): + overlay._set_virtual(key, value) + + def __delitem__(self, key): + if key not in self: + raise KeyError(key) + if key in self._virtual_keys: + raise KeyError("Virtual keys cannot be deleted: %s" % key) + + if self._source and key in self._source: + # Not virtual, but present in source -> Restore virtual value + dict.__delitem__(self, key) + self._set_virtual(key, self._source[key]) + else: # not virtual, not present in source. This is OUR value + self._on_change(key, None) + dict.__delitem__(self, key) + for overlay in self._iter_overlays(): + overlay._delete_virtual(key) + + def _set_virtual(self, key, value): + """ Recursively set or update virtual keys. Do nothing if non-virtual + value is present. """ + if key in self and key not in self._virtual_keys: + return # Do nothing for non-virtual keys. + + self._virtual_keys.add(key) + if key in self and self[key] is not value: + self._on_change(key, value) + dict.__setitem__(self, key, value) + for overlay in self._iter_overlays(): + overlay._set_virtual(key, value) + + def _delete_virtual(self, key): + """ Recursively delete virtual entry. Do nothing if key is not virtual. + """ + if key not in self._virtual_keys: + return # Do nothing for non-virtual keys. + + if key in self: + self._on_change(key, None) + dict.__delitem__(self, key) + self._virtual_keys.discard(key) + for overlay in self._iter_overlays(): + overlay._delete_virtual(key) + + def _on_change(self, key, value): + for cb in self._change_listener: + if cb(self, key, value): + return True + + def _add_change_listener(self, func): + self._change_listener.append(func) + return func + + def meta_get(self, key, metafield, default=None): + """ Return the value of a meta field for a key. """ + return self._meta.get(key, {}).get(metafield, default) + + def meta_set(self, key, metafield, value): + """ Set the meta field for a key to a new value. """ + self._meta.setdefault(key, {})[metafield] = value + + def meta_list(self, key): + """ Return an iterable of meta field names defined for a key. """ + return self._meta.get(key, {}).keys() + + def _define(self, key, default=_UNSET, help=_UNSET, validate=_UNSET): + """ (Unstable) Shortcut for plugins to define own config parameters. """ + if default is not _UNSET: + self.setdefault(key, default) + if help is not _UNSET: + self.meta_set(key, 'help', help) + if validate is not _UNSET: + self.meta_set(key, 'validate', validate) + + def _iter_overlays(self): + for ref in self._overlays: + overlay = ref() + if overlay is not None: + yield overlay + + def _make_overlay(self): + """ (Unstable) Create a new overlay that acts like a chained map: Values + missing in the overlay are copied from the source map. Both maps + share the same meta entries. + + Entries that were copied from the source are called 'virtual'. You + can not delete virtual keys, but overwrite them, which turns them + into non-virtual entries. Setting keys on an overlay never affects + its source, but may affect any number of child overlays. + + Other than collections.ChainMap or most other implementations, this + approach does not resolve missing keys on demand, but instead + actively copies all values from the source to the overlay and keeps + track of virtual and non-virtual keys internally. This removes any + lookup-overhead. Read-access is as fast as a build-in dict for both + virtual and non-virtual keys. + + Changes are propagated recursively and depth-first. A failing + on-change handler in an overlay stops the propagation of virtual + values and may result in an partly updated tree. Take extra care + here and make sure that on-change handlers never fail. + + Used by Route.config + """ + # Cleanup dead references + self._overlays[:] = [ref for ref in self._overlays if ref() is not None] + + overlay = ConfigDict() + overlay._meta = self._meta + overlay._source = self + self._overlays.append(weakref.ref(overlay)) + for key in self: + overlay._set_virtual(key, self[key]) + return overlay + + + + +class AppStack(list): + """ A stack-like list. Calling it returns the head of the stack. """ + + def __call__(self): + """ Return the current default application. """ + return self.default + + def push(self, value=None): + """ Add a new :class:`Bottle` instance to the stack """ + if not isinstance(value, Bottle): + value = Bottle() + self.append(value) + return value + new_app = push + + @property + def default(self): + try: + return self[-1] + except IndexError: + return self.push() + + +class WSGIFileWrapper(object): + def __init__(self, fp, buffer_size=1024 * 64): + self.fp, self.buffer_size = fp, buffer_size + for attr in 'fileno', 'close', 'read', 'readlines', 'tell', 'seek': + if hasattr(fp, attr): setattr(self, attr, getattr(fp, attr)) + + def __iter__(self): + buff, read = self.buffer_size, self.read + part = read(buff) + while part: + yield part + part = read(buff) + + +class _closeiter(object): + """ This only exists to be able to attach a .close method to iterators that + do not support attribute assignment (most of itertools). """ + + def __init__(self, iterator, close=None): + self.iterator = iterator + self.close_callbacks = makelist(close) + + def __iter__(self): + return iter(self.iterator) + + def close(self): + for func in self.close_callbacks: + func() + + +class ResourceManager(object): + """ This class manages a list of search paths and helps to find and open + application-bound resources (files). + + :param base: default value for :meth:`add_path` calls. + :param opener: callable used to open resources. + :param cachemode: controls which lookups are cached. One of 'all', + 'found' or 'none'. + """ + + def __init__(self, base='./', opener=open, cachemode='all'): + self.opener = opener + self.base = base + self.cachemode = cachemode + + #: A list of search paths. See :meth:`add_path` for details. + self.path = [] + #: A cache for resolved paths. ``res.cache.clear()`` clears the cache. + self.cache = {} + + def add_path(self, path, base=None, index=None, create=False): + """ Add a new path to the list of search paths. Return False if the + path does not exist. + + :param path: The new search path. Relative paths are turned into + an absolute and normalized form. If the path looks like a file + (not ending in `/`), the filename is stripped off. + :param base: Path used to absolutize relative search paths. + Defaults to :attr:`base` which defaults to ``os.getcwd()``. + :param index: Position within the list of search paths. Defaults + to last index (appends to the list). + + The `base` parameter makes it easy to reference files installed + along with a python module or package:: + + res.add_path('./resources/', __file__) + """ + base = os.path.abspath(os.path.dirname(base or self.base)) + path = os.path.abspath(os.path.join(base, os.path.dirname(path))) + path += os.sep + if path in self.path: + self.path.remove(path) + if create and not os.path.isdir(path): + os.makedirs(path) + if index is None: + self.path.append(path) + else: + self.path.insert(index, path) + self.cache.clear() + return os.path.exists(path) + + def __iter__(self): + """ Iterate over all existing files in all registered paths. """ + search = self.path[:] + while search: + path = search.pop() + if not os.path.isdir(path): continue + for name in os.listdir(path): + full = os.path.join(path, name) + if os.path.isdir(full): search.append(full) + else: yield full + + def lookup(self, name): + """ Search for a resource and return an absolute file path, or `None`. + + The :attr:`path` list is searched in order. The first match is + returned. Symlinks are followed. The result is cached to speed up + future lookups. """ + if name not in self.cache or DEBUG: + for path in self.path: + fpath = os.path.join(path, name) + if os.path.isfile(fpath): + if self.cachemode in ('all', 'found'): + self.cache[name] = fpath + return fpath + if self.cachemode == 'all': + self.cache[name] = None + return self.cache[name] + + def open(self, name, mode='r', *args, **kwargs): + """ Find a resource and return a file object, or raise IOError. """ + fname = self.lookup(name) + if not fname: raise IOError("Resource %r not found." % name) + return self.opener(fname, mode=mode, *args, **kwargs) + + +class FileUpload(object): + def __init__(self, fileobj, name, filename, headers=None): + """ Wrapper for file uploads. """ + #: Open file(-like) object (BytesIO buffer or temporary file) + self.file = fileobj + #: Name of the upload form field + self.name = name + #: Raw filename as sent by the client (may contain unsafe characters) + self.raw_filename = filename + #: A :class:`HeaderDict` with additional headers (e.g. content-type) + self.headers = HeaderDict(headers) if headers else HeaderDict() + + content_type = HeaderProperty('Content-Type') + content_length = HeaderProperty('Content-Length', reader=int, default=-1) + + def get_header(self, name, default=None): + """ Return the value of a header within the multipart part. """ + return self.headers.get(name, default) + + @cached_property + def filename(self): + """ Name of the file on the client file system, but normalized to ensure + file system compatibility. An empty filename is returned as 'empty'. + + Only ASCII letters, digits, dashes, underscores and dots are + allowed in the final filename. Accents are removed, if possible. + Whitespace is replaced by a single dash. Leading or tailing dots + or dashes are removed. The filename is limited to 255 characters. + """ + fname = self.raw_filename + if not isinstance(fname, unicode): + fname = fname.decode('utf8', 'ignore') + fname = normalize('NFKD', fname) + fname = fname.encode('ASCII', 'ignore').decode('ASCII') + fname = os.path.basename(fname.replace('\\', os.path.sep)) + fname = re.sub(r'[^a-zA-Z0-9-_.\s]', '', fname).strip() + fname = re.sub(r'[-\s]+', '-', fname).strip('.-') + return fname[:255] or 'empty' + + def _copy_file(self, fp, chunk_size=2 ** 16): + read, write, offset = self.file.read, fp.write, self.file.tell() + while 1: + buf = read(chunk_size) + if not buf: break + write(buf) + self.file.seek(offset) + + def save(self, destination, overwrite=False, chunk_size=2 ** 16): + """ Save file to disk or copy its content to an open file(-like) object. + If *destination* is a directory, :attr:`filename` is added to the + path. Existing files are not overwritten by default (IOError). + + :param destination: File path, directory or file(-like) object. + :param overwrite: If True, replace existing files. (default: False) + :param chunk_size: Bytes to read at a time. (default: 64kb) + """ + if isinstance(destination, basestring): # Except file-likes here + if os.path.isdir(destination): + destination = os.path.join(destination, self.filename) + if not overwrite and os.path.exists(destination): + raise IOError('File exists.') + with open(destination, 'wb') as fp: + self._copy_file(fp, chunk_size) + else: + self._copy_file(destination, chunk_size) + +############################################################################### +# Application Helper ########################################################### +############################################################################### + + +def abort(code=500, text='Unknown Error.'): + """ Aborts execution and causes a HTTP error. """ + raise HTTPError(code, text) + + +def redirect(url, code=None): + """ Aborts execution and causes a 303 or 302 redirect, depending on + the HTTP protocol version. """ + if not code: + code = 303 if request.get('SERVER_PROTOCOL') == "HTTP/1.1" else 302 + res = response.copy(cls=HTTPResponse) + res.status = code + res.body = "" + res.set_header('Location', urljoin(request.url, url)) + raise res + + +def _rangeiter(fp, offset, limit, bufsize=1024 * 1024): + """ Yield chunks from a range in a file. """ + fp.seek(offset) + while limit > 0: + part = fp.read(min(limit, bufsize)) + if not part: + break + limit -= len(part) + yield part + + +def static_file(filename, root, + mimetype=True, + download=False, + charset='UTF-8', + etag=None, + headers=None): + """ Open a file in a safe way and return an instance of :exc:`HTTPResponse` + that can be sent back to the client. + + :param filename: Name or path of the file to send, relative to ``root``. + :param root: Root path for file lookups. Should be an absolute directory + path. + :param mimetype: Provide the content-type header (default: guess from + file extension) + :param download: If True, ask the browser to open a `Save as...` dialog + instead of opening the file with the associated program. You can + specify a custom filename as a string. If not specified, the + original filename is used (default: False). + :param charset: The charset for files with a ``text/*`` mime-type. + (default: UTF-8) + :param etag: Provide a pre-computed ETag header. If set to ``False``, + ETag handling is disabled. (default: auto-generate ETag header) + :param headers: Additional headers dict to add to the response. + + While checking user input is always a good idea, this function provides + additional protection against malicious ``filename`` parameters from + breaking out of the ``root`` directory and leaking sensitive information + to an attacker. + + Read-protected files or files outside of the ``root`` directory are + answered with ``403 Access Denied``. Missing files result in a + ``404 Not Found`` response. Conditional requests (``If-Modified-Since``, + ``If-None-Match``) are answered with ``304 Not Modified`` whenever + possible. ``HEAD`` and ``Range`` requests (used by download managers to + check or continue partial downloads) are also handled automatically. + + """ + + root = os.path.join(os.path.abspath(root), '') + filename = os.path.abspath(os.path.join(root, filename.strip('/\\'))) + headers = headers.copy() if headers else {} + + if not filename.startswith(root): + return HTTPError(403, "Access denied.") + if not os.path.exists(filename) or not os.path.isfile(filename): + return HTTPError(404, "File does not exist.") + if not os.access(filename, os.R_OK): + return HTTPError(403, "You do not have permission to access this file.") + + if mimetype is True: + if download and download is not True: + mimetype, encoding = mimetypes.guess_type(download) + else: + mimetype, encoding = mimetypes.guess_type(filename) + if encoding: + headers['Content-Encoding'] = encoding + + if mimetype: + if (mimetype[:5] == 'text/' or mimetype == 'application/javascript')\ + and charset and 'charset' not in mimetype: + mimetype += '; charset=%s' % charset + headers['Content-Type'] = mimetype + + if download: + download = os.path.basename(filename if download is True else download) + headers['Content-Disposition'] = 'attachment; filename="%s"' % download + + stats = os.stat(filename) + headers['Content-Length'] = clen = stats.st_size + headers['Last-Modified'] = email.utils.formatdate(stats.st_mtime, + usegmt=True) + headers['Date'] = email.utils.formatdate(time.time(), usegmt=True) + + getenv = request.environ.get + + if etag is None: + etag = '%d:%d:%d:%d:%s' % (stats.st_dev, stats.st_ino, stats.st_mtime, + clen, filename) + etag = hashlib.sha1(tob(etag)).hexdigest() + + if etag: + headers['ETag'] = etag + check = getenv('HTTP_IF_NONE_MATCH') + if check and check == etag: + return HTTPResponse(status=304, **headers) + + ims = getenv('HTTP_IF_MODIFIED_SINCE') + if ims: + ims = parse_date(ims.split(";")[0].strip()) + if ims is not None and ims >= int(stats.st_mtime): + return HTTPResponse(status=304, **headers) + + body = '' if request.method == 'HEAD' else open(filename, 'rb') + + headers["Accept-Ranges"] = "bytes" + range_header = getenv('HTTP_RANGE') + if range_header: + ranges = list(parse_range_header(range_header, clen)) + if not ranges: + return HTTPError(416, "Requested Range Not Satisfiable") + offset, end = ranges[0] + rlen = end - offset + headers["Content-Range"] = "bytes %d-%d/%d" % (offset, end - 1, clen) + headers["Content-Length"] = str(rlen) + if body: body = _closeiter(_rangeiter(body, offset, rlen), body.close) + return HTTPResponse(body, status=206, **headers) + return HTTPResponse(body, **headers) + +############################################################################### +# HTTP Utilities and MISC (TODO) ############################################### +############################################################################### + + +def debug(mode=True): + """ Change the debug level. + There is only one debug level supported at the moment.""" + global DEBUG + if mode: warnings.simplefilter('default') + DEBUG = bool(mode) + + +def http_date(value): + if isinstance(value, basestring): + return value + if isinstance(value, datetime): + # aware datetime.datetime is converted to UTC time + # naive datetime.datetime is treated as UTC time + value = value.utctimetuple() + elif isinstance(value, datedate): + # datetime.date is naive, and is treated as UTC time + value = value.timetuple() + if not isinstance(value, (int, float)): + # convert struct_time in UTC to UNIX timestamp + value = calendar.timegm(value) + return email.utils.formatdate(value, usegmt=True) + + +def parse_date(ims): + """ Parse rfc1123, rfc850 and asctime timestamps and return UTC epoch. """ + try: + ts = email.utils.parsedate_tz(ims) + return calendar.timegm(ts[:8] + (0, )) - (ts[9] or 0) + except (TypeError, ValueError, IndexError, OverflowError): + return None + + +def parse_auth(header): + """ Parse rfc2617 HTTP authentication header string (basic) and return (user,pass) tuple or None""" + try: + method, data = header.split(None, 1) + if method.lower() == 'basic': + user, pwd = touni(base64.b64decode(tob(data))).split(':', 1) + return user, pwd + except (KeyError, ValueError): + return None + + +def parse_range_header(header, maxlen=0): + """ Yield (start, end) ranges parsed from a HTTP Range header. Skip + unsatisfiable ranges. The end index is non-inclusive.""" + if not header or header[:6] != 'bytes=': return + ranges = [r.split('-', 1) for r in header[6:].split(',') if '-' in r] + for start, end in ranges: + try: + if not start: # bytes=-100 -> last 100 bytes + start, end = max(0, maxlen - int(end)), maxlen + elif not end: # bytes=100- -> all but the first 99 bytes + start, end = int(start), maxlen + else: # bytes=100-200 -> bytes 100-200 (inclusive) + start, end = int(start), min(int(end) + 1, maxlen) + if 0 <= start < end <= maxlen: + yield start, end + except ValueError: + pass + + +#: Header tokenizer used by _parse_http_header() +_hsplit = re.compile('(?:(?:"((?:[^"\\\\]|\\\\.)*)")|([^;,=]+))([;,=]?)').findall + +def _parse_http_header(h): + """ Parses a typical multi-valued and parametrised HTTP header (e.g. Accept headers) and returns a list of values + and parameters. For non-standard or broken input, this implementation may return partial results. + :param h: A header string (e.g. ``text/html,text/plain;q=0.9,*/*;q=0.8``) + :return: List of (value, params) tuples. The second element is a (possibly empty) dict. + """ + values = [] + if '"' not in h: # INFO: Fast path without regexp (~2x faster) + for value in h.split(','): + parts = value.split(';') + values.append((parts[0].strip(), {})) + for attr in parts[1:]: + name, value = attr.split('=', 1) + values[-1][1][name.strip()] = value.strip() + else: + lop, key, attrs = ',', None, {} + for quoted, plain, tok in _hsplit(h): + value = plain.strip() if plain else quoted.replace('\\"', '"') + if lop == ',': + attrs = {} + values.append((value, attrs)) + elif lop == ';': + if tok == '=': + key = value + else: + attrs[value] = '' + elif lop == '=' and key: + attrs[key] = value + key = None + lop = tok + return values + + +def _parse_qsl(qs): + r = [] + for pair in qs.split('&'): + if not pair: continue + nv = pair.split('=', 1) + if len(nv) != 2: nv.append('') + key = urlunquote(nv[0].replace('+', ' ')) + value = urlunquote(nv[1].replace('+', ' ')) + r.append((key, value)) + return r + + +def _lscmp(a, b): + """ Compares two strings in a cryptographically safe way: + Runtime is not affected by length of common prefix. """ + return not sum(0 if x == y else 1 + for x, y in zip(a, b)) and len(a) == len(b) + + +def cookie_encode(data, key, digestmod=None): + """ Encode and sign a pickle-able object. Return a (byte) string """ + depr(0, 13, "cookie_encode() will be removed soon.", + "Do not use this API directly.") + digestmod = digestmod or hashlib.sha256 + msg = base64.b64encode(pickle.dumps(data, -1)) + sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=digestmod).digest()) + return tob('!') + sig + tob('?') + msg + + +def cookie_decode(data, key, digestmod=None): + """ Verify and decode an encoded string. Return an object or None.""" + depr(0, 13, "cookie_decode() will be removed soon.", + "Do not use this API directly.") + data = tob(data) + if cookie_is_encoded(data): + sig, msg = data.split(tob('?'), 1) + digestmod = digestmod or hashlib.sha256 + hashed = hmac.new(tob(key), msg, digestmod=digestmod).digest() + if _lscmp(sig[1:], base64.b64encode(hashed)): + return pickle.loads(base64.b64decode(msg)) + return None + + +def cookie_is_encoded(data): + """ Return True if the argument looks like a encoded cookie.""" + depr(0, 13, "cookie_is_encoded() will be removed soon.", + "Do not use this API directly.") + return bool(data.startswith(tob('!')) and tob('?') in data) + + +def html_escape(string): + """ Escape HTML special characters ``&<>`` and quotes ``'"``. """ + return string.replace('&', '&').replace('<', '<').replace('>', '>')\ + .replace('"', '"').replace("'", ''') + + +def html_quote(string): + """ Escape and quote a string to be used as an HTTP attribute.""" + return '"%s"' % html_escape(string).replace('\n', ' ')\ + .replace('\r', ' ').replace('\t', ' ') + + +def yieldroutes(func): + """ Return a generator for routes that match the signature (name, args) + of the func parameter. This may yield more than one route if the function + takes optional keyword arguments. The output is best described by example:: + + a() -> '/a' + b(x, y) -> '/b//' + c(x, y=5) -> '/c/' and '/c//' + d(x=5, y=6) -> '/d' and '/d/' and '/d//' + """ + path = '/' + func.__name__.replace('__', '/').lstrip('/') + spec = getargspec(func) + argc = len(spec[0]) - len(spec[3] or []) + path += ('/<%s>' * argc) % tuple(spec[0][:argc]) + yield path + for arg in spec[0][argc:]: + path += '/<%s>' % arg + yield path + + +def path_shift(script_name, path_info, shift=1): + """ Shift path fragments from PATH_INFO to SCRIPT_NAME and vice versa. + + :return: The modified paths. + :param script_name: The SCRIPT_NAME path. + :param script_name: The PATH_INFO path. + :param shift: The number of path fragments to shift. May be negative to + change the shift direction. (default: 1) + """ + if shift == 0: return script_name, path_info + pathlist = path_info.strip('/').split('/') + scriptlist = script_name.strip('/').split('/') + if pathlist and pathlist[0] == '': pathlist = [] + if scriptlist and scriptlist[0] == '': scriptlist = [] + if 0 < shift <= len(pathlist): + moved = pathlist[:shift] + scriptlist = scriptlist + moved + pathlist = pathlist[shift:] + elif 0 > shift >= -len(scriptlist): + moved = scriptlist[shift:] + pathlist = moved + pathlist + scriptlist = scriptlist[:shift] + else: + empty = 'SCRIPT_NAME' if shift < 0 else 'PATH_INFO' + raise AssertionError("Cannot shift. Nothing left from %s" % empty) + new_script_name = '/' + '/'.join(scriptlist) + new_path_info = '/' + '/'.join(pathlist) + if path_info.endswith('/') and pathlist: new_path_info += '/' + return new_script_name, new_path_info + + +def auth_basic(check, realm="private", text="Access denied"): + """ Callback decorator to require HTTP auth (basic). + TODO: Add route(check_auth=...) parameter. """ + + def decorator(func): + + @functools.wraps(func) + def wrapper(*a, **ka): + user, password = request.auth or (None, None) + if user is None or not check(user, password): + err = HTTPError(401, text) + err.add_header('WWW-Authenticate', 'Basic realm="%s"' % realm) + return err + return func(*a, **ka) + + return wrapper + + return decorator + +# Shortcuts for common Bottle methods. +# They all refer to the current default application. + + +def make_default_app_wrapper(name): + """ Return a callable that relays calls to the current default app. """ + + @functools.wraps(getattr(Bottle, name)) + def wrapper(*a, **ka): + return getattr(app(), name)(*a, **ka) + + return wrapper + + +route = make_default_app_wrapper('route') +get = make_default_app_wrapper('get') +post = make_default_app_wrapper('post') +put = make_default_app_wrapper('put') +delete = make_default_app_wrapper('delete') +patch = make_default_app_wrapper('patch') +error = make_default_app_wrapper('error') +mount = make_default_app_wrapper('mount') +hook = make_default_app_wrapper('hook') +install = make_default_app_wrapper('install') +uninstall = make_default_app_wrapper('uninstall') +url = make_default_app_wrapper('get_url') + +############################################################################### +# Server Adapter ############################################################### +############################################################################### + +# Before you edit or add a server adapter, please read: +# - https://github.com/bottlepy/bottle/pull/647#issuecomment-60152870 +# - https://github.com/bottlepy/bottle/pull/865#issuecomment-242795341 + +class ServerAdapter(object): + quiet = False + + def __init__(self, host='127.0.0.1', port=8080, **options): + self.options = options + self.host = host + self.port = int(port) + + def run(self, handler): # pragma: no cover + pass + + def __repr__(self): + args = ', '.join('%s=%s' % (k, repr(v)) + for k, v in self.options.items()) + return "%s(%s)" % (self.__class__.__name__, args) + + +class CGIServer(ServerAdapter): + quiet = True + + def run(self, handler): # pragma: no cover + from wsgiref.handlers import CGIHandler + + def fixed_environ(environ, start_response): + environ.setdefault('PATH_INFO', '') + return handler(environ, start_response) + + CGIHandler().run(fixed_environ) + + +class FlupFCGIServer(ServerAdapter): + def run(self, handler): # pragma: no cover + import flup.server.fcgi + self.options.setdefault('bindAddress', (self.host, self.port)) + flup.server.fcgi.WSGIServer(handler, **self.options).run() + + +class WSGIRefServer(ServerAdapter): + def run(self, app): # pragma: no cover + from wsgiref.simple_server import make_server + from wsgiref.simple_server import WSGIRequestHandler, WSGIServer + import socket + + class FixedHandler(WSGIRequestHandler): + def address_string(self): # Prevent reverse DNS lookups please. + return self.client_address[0] + + def log_request(*args, **kw): + if not self.quiet: + return WSGIRequestHandler.log_request(*args, **kw) + + handler_cls = self.options.get('handler_class', FixedHandler) + server_cls = self.options.get('server_class', WSGIServer) + + if ':' in self.host: # Fix wsgiref for IPv6 addresses. + if getattr(server_cls, 'address_family') == socket.AF_INET: + + class server_cls(server_cls): + address_family = socket.AF_INET6 + + self.srv = make_server(self.host, self.port, app, server_cls, + handler_cls) + self.port = self.srv.server_port # update port actual port (0 means random) + try: + self.srv.serve_forever() + except KeyboardInterrupt: + self.srv.server_close() # Prevent ResourceWarning: unclosed socket + raise + + +class CherryPyServer(ServerAdapter): + def run(self, handler): # pragma: no cover + depr(0, 13, "The wsgi server part of cherrypy was split into a new " + "project called 'cheroot'.", "Use the 'cheroot' server " + "adapter instead of cherrypy.") + from cherrypy import wsgiserver # This will fail for CherryPy >= 9 + + self.options['bind_addr'] = (self.host, self.port) + self.options['wsgi_app'] = handler + + certfile = self.options.get('certfile') + if certfile: + del self.options['certfile'] + keyfile = self.options.get('keyfile') + if keyfile: + del self.options['keyfile'] + + server = wsgiserver.CherryPyWSGIServer(**self.options) + if certfile: + server.ssl_certificate = certfile + if keyfile: + server.ssl_private_key = keyfile + + try: + server.start() + finally: + server.stop() + + +class CherootServer(ServerAdapter): + def run(self, handler): # pragma: no cover + from cheroot import wsgi + from cheroot.ssl import builtin + self.options['bind_addr'] = (self.host, self.port) + self.options['wsgi_app'] = handler + certfile = self.options.pop('certfile', None) + keyfile = self.options.pop('keyfile', None) + chainfile = self.options.pop('chainfile', None) + server = wsgi.Server(**self.options) + if certfile and keyfile: + server.ssl_adapter = builtin.BuiltinSSLAdapter( + certfile, keyfile, chainfile) + try: + server.start() + finally: + server.stop() + + +class WaitressServer(ServerAdapter): + def run(self, handler): + from waitress import serve + serve(handler, host=self.host, port=self.port, _quiet=self.quiet, **self.options) + + +class PasteServer(ServerAdapter): + def run(self, handler): # pragma: no cover + from paste import httpserver + from paste.translogger import TransLogger + handler = TransLogger(handler, setup_console_handler=(not self.quiet)) + httpserver.serve(handler, + host=self.host, + port=str(self.port), **self.options) + + +class MeinheldServer(ServerAdapter): + def run(self, handler): + from meinheld import server + server.listen((self.host, self.port)) + server.run(handler) + + +class FapwsServer(ServerAdapter): + """ Extremely fast webserver using libev. See https://github.com/william-os4y/fapws3 """ + + def run(self, handler): # pragma: no cover + depr(0, 13, "fapws3 is not maintained and support will be dropped.") + import fapws._evwsgi as evwsgi + from fapws import base, config + port = self.port + if float(config.SERVER_IDENT[-2:]) > 0.4: + # fapws3 silently changed its API in 0.5 + port = str(port) + evwsgi.start(self.host, port) + # fapws3 never releases the GIL. Complain upstream. I tried. No luck. + if 'BOTTLE_CHILD' in os.environ and not self.quiet: + _stderr("WARNING: Auto-reloading does not work with Fapws3.") + _stderr(" (Fapws3 breaks python thread support)") + evwsgi.set_base_module(base) + + def app(environ, start_response): + environ['wsgi.multiprocess'] = False + return handler(environ, start_response) + + evwsgi.wsgi_cb(('', app)) + evwsgi.run() + + +class TornadoServer(ServerAdapter): + """ The super hyped asynchronous server by facebook. Untested. """ + + def run(self, handler): # pragma: no cover + import tornado.wsgi, tornado.httpserver, tornado.ioloop + container = tornado.wsgi.WSGIContainer(handler) + server = tornado.httpserver.HTTPServer(container) + server.listen(port=self.port, address=self.host) + tornado.ioloop.IOLoop.instance().start() + + +class AppEngineServer(ServerAdapter): + """ Adapter for Google App Engine. """ + quiet = True + + def run(self, handler): + depr(0, 13, "AppEngineServer no longer required", + "Configure your application directly in your app.yaml") + from google.appengine.ext.webapp import util + # A main() function in the handler script enables 'App Caching'. + # Lets makes sure it is there. This _really_ improves performance. + module = sys.modules.get('__main__') + if module and not hasattr(module, 'main'): + module.main = lambda: util.run_wsgi_app(handler) + util.run_wsgi_app(handler) + + +class TwistedServer(ServerAdapter): + """ Untested. """ + + def run(self, handler): + from twisted.web import server, wsgi + from twisted.python.threadpool import ThreadPool + from twisted.internet import reactor + thread_pool = ThreadPool() + thread_pool.start() + reactor.addSystemEventTrigger('after', 'shutdown', thread_pool.stop) + factory = server.Site(wsgi.WSGIResource(reactor, thread_pool, handler)) + reactor.listenTCP(self.port, factory, interface=self.host) + if not reactor.running: + reactor.run() + + +class DieselServer(ServerAdapter): + """ Untested. """ + + def run(self, handler): + depr(0, 13, "Diesel is not tested or supported and will be removed.") + from diesel.protocols.wsgi import WSGIApplication + app = WSGIApplication(handler, port=self.port) + app.run() + + +class GeventServer(ServerAdapter): + """ Untested. Options: + + * See gevent.wsgi.WSGIServer() documentation for more options. + """ + + def run(self, handler): + from gevent import pywsgi, local + if not isinstance(threading.local(), local.local): + msg = "Bottle requires gevent.monkey.patch_all() (before import)" + raise RuntimeError(msg) + if self.quiet: + self.options['log'] = None + address = (self.host, self.port) + server = pywsgi.WSGIServer(address, handler, **self.options) + if 'BOTTLE_CHILD' in os.environ: + import signal + signal.signal(signal.SIGINT, lambda s, f: server.stop()) + server.serve_forever() + + +class GunicornServer(ServerAdapter): + """ Untested. See http://gunicorn.org/configure.html for options. """ + + def run(self, handler): + from gunicorn.app.base import BaseApplication + + if self.host.startswith("unix:"): + config = {'bind': self.host} + else: + config = {'bind': "%s:%d" % (self.host, self.port)} + + config.update(self.options) + + class GunicornApplication(BaseApplication): + def load_config(self): + for key, value in config.items(): + self.cfg.set(key, value) + + def load(self): + return handler + + GunicornApplication().run() + + +class EventletServer(ServerAdapter): + """ Untested. Options: + + * `backlog` adjust the eventlet backlog parameter which is the maximum + number of queued connections. Should be at least 1; the maximum + value is system-dependent. + * `family`: (default is 2) socket family, optional. See socket + documentation for available families. + """ + + def run(self, handler): + from eventlet import wsgi, listen, patcher + if not patcher.is_monkey_patched(os): + msg = "Bottle requires eventlet.monkey_patch() (before import)" + raise RuntimeError(msg) + socket_args = {} + for arg in ('backlog', 'family'): + try: + socket_args[arg] = self.options.pop(arg) + except KeyError: + pass + address = (self.host, self.port) + try: + wsgi.server(listen(address, **socket_args), handler, + log_output=(not self.quiet)) + except TypeError: + # Fallback, if we have old version of eventlet + wsgi.server(listen(address), handler) + + +class BjoernServer(ServerAdapter): + """ Fast server written in C: https://github.com/jonashaag/bjoern """ + + def run(self, handler): + from bjoern import run + run(handler, self.host, self.port, reuse_port=True) + +class AsyncioServerAdapter(ServerAdapter): + """ Extend ServerAdapter for adding custom event loop """ + def get_event_loop(self): + pass + +class AiohttpServer(AsyncioServerAdapter): + """ Asynchronous HTTP client/server framework for asyncio + https://pypi.python.org/pypi/aiohttp/ + https://pypi.org/project/aiohttp-wsgi/ + """ + + def get_event_loop(self): + import asyncio + return asyncio.new_event_loop() + + def run(self, handler): + import asyncio + from aiohttp_wsgi.wsgi import serve + self.loop = self.get_event_loop() + asyncio.set_event_loop(self.loop) + + if 'BOTTLE_CHILD' in os.environ: + import signal + signal.signal(signal.SIGINT, lambda s, f: self.loop.stop()) + + serve(handler, host=self.host, port=self.port) + + +class AiohttpUVLoopServer(AiohttpServer): + """uvloop + https://github.com/MagicStack/uvloop + """ + def get_event_loop(self): + import uvloop + return uvloop.new_event_loop() + +class AutoServer(ServerAdapter): + """ Untested. """ + adapters = [WaitressServer, PasteServer, TwistedServer, CherryPyServer, + CherootServer, WSGIRefServer] + + def run(self, handler): + for sa in self.adapters: + try: + return sa(self.host, self.port, **self.options).run(handler) + except ImportError: + pass + + +server_names = { + 'cgi': CGIServer, + 'flup': FlupFCGIServer, + 'wsgiref': WSGIRefServer, + 'waitress': WaitressServer, + 'cherrypy': CherryPyServer, + 'cheroot': CherootServer, + 'paste': PasteServer, + 'fapws3': FapwsServer, + 'tornado': TornadoServer, + 'gae': AppEngineServer, + 'twisted': TwistedServer, + 'diesel': DieselServer, + 'meinheld': MeinheldServer, + 'gunicorn': GunicornServer, + 'eventlet': EventletServer, + 'gevent': GeventServer, + 'bjoern': BjoernServer, + 'aiohttp': AiohttpServer, + 'uvloop': AiohttpUVLoopServer, + 'auto': AutoServer, +} + +############################################################################### +# Application Control ########################################################## +############################################################################### + + +def load(target, **namespace): + """ Import a module or fetch an object from a module. + + * ``package.module`` returns `module` as a module object. + * ``pack.mod:name`` returns the module variable `name` from `pack.mod`. + * ``pack.mod:func()`` calls `pack.mod.func()` and returns the result. + + The last form accepts not only function calls, but any type of + expression. Keyword arguments passed to this function are available as + local variables. Example: ``import_string('re:compile(x)', x='[a-z]')`` + """ + module, target = target.split(":", 1) if ':' in target else (target, None) + if module not in sys.modules: __import__(module) + if not target: return sys.modules[module] + if target.isalnum(): return getattr(sys.modules[module], target) + package_name = module.split('.')[0] + namespace[package_name] = sys.modules[package_name] + return eval('%s.%s' % (module, target), namespace) + + +def load_app(target): + """ Load a bottle application from a module and make sure that the import + does not affect the current default application, but returns a separate + application object. See :func:`load` for the target parameter. """ + global NORUN + NORUN, nr_old = True, NORUN + tmp = default_app.push() # Create a new "default application" + try: + rv = load(target) # Import the target module + return rv if callable(rv) else tmp + finally: + default_app.remove(tmp) # Remove the temporary added default application + NORUN = nr_old + + +_debug = debug + + +def run(app=None, + server='wsgiref', + host='127.0.0.1', + port=8080, + interval=1, + reloader=False, + quiet=False, + plugins=None, + debug=None, + config=None, **kargs): + """ Start a server instance. This method blocks until the server terminates. + + :param app: WSGI application or target string supported by + :func:`load_app`. (default: :func:`default_app`) + :param server: Server adapter to use. See :data:`server_names` keys + for valid names or pass a :class:`ServerAdapter` subclass. + (default: `wsgiref`) + :param host: Server address to bind to. Pass ``0.0.0.0`` to listens on + all interfaces including the external one. (default: 127.0.0.1) + :param port: Server port to bind to. Values below 1024 require root + privileges. (default: 8080) + :param reloader: Start auto-reloading server? (default: False) + :param interval: Auto-reloader interval in seconds (default: 1) + :param quiet: Suppress output to stdout and stderr? (default: False) + :param options: Options passed to the server adapter. + """ + if NORUN: return + if reloader and not os.environ.get('BOTTLE_CHILD'): + import subprocess + fd, lockfile = tempfile.mkstemp(prefix='bottle.', suffix='.lock') + environ = os.environ.copy() + environ['BOTTLE_CHILD'] = 'true' + environ['BOTTLE_LOCKFILE'] = lockfile + args = [sys.executable] + sys.argv + # If a package was loaded with `python -m`, then `sys.argv` needs to be + # restored to the original value, or imports might break. See #1336 + if getattr(sys.modules.get('__main__'), '__package__', None): + args[1:1] = ["-m", sys.modules['__main__'].__package__] + + try: + os.close(fd) # We never write to this file + while os.path.exists(lockfile): + p = subprocess.Popen(args, env=environ) + while p.poll() is None: + os.utime(lockfile, None) # Tell child we are still alive + time.sleep(interval) + if p.returncode == 3: # Child wants to be restarted + continue + sys.exit(p.returncode) + except KeyboardInterrupt: + pass + finally: + if os.path.exists(lockfile): + os.unlink(lockfile) + return + + try: + if debug is not None: _debug(debug) + app = app or default_app() + if isinstance(app, basestring): + app = load_app(app) + if not callable(app): + raise ValueError("Application is not callable: %r" % app) + + for plugin in plugins or []: + if isinstance(plugin, basestring): + plugin = load(plugin) + app.install(plugin) + + if config: + app.config.update(config) + + if server in server_names: + server = server_names.get(server) + if isinstance(server, basestring): + server = load(server) + if isinstance(server, type): + server = server(host=host, port=port, **kargs) + if not isinstance(server, ServerAdapter): + raise ValueError("Unknown or unsupported server: %r" % server) + + server.quiet = server.quiet or quiet + if not server.quiet: + _stderr("Bottle v%s server starting up (using %s)..." % + (__version__, repr(server))) + if server.host.startswith("unix:"): + _stderr("Listening on %s" % server.host) + else: + _stderr("Listening on http://%s:%d/" % + (server.host, server.port)) + _stderr("Hit Ctrl-C to quit.\n") + + if reloader: + lockfile = os.environ.get('BOTTLE_LOCKFILE') + bgcheck = FileCheckerThread(lockfile, interval) + with bgcheck: + server.run(app) + if bgcheck.status == 'reload': + sys.exit(3) + else: + server.run(app) + except KeyboardInterrupt: + pass + except (SystemExit, MemoryError): + raise + except: + if not reloader: raise + if not getattr(server, 'quiet', quiet): + print_exc() + time.sleep(interval) + sys.exit(3) + + +class FileCheckerThread(threading.Thread): + """ Interrupt main-thread as soon as a changed module file is detected, + the lockfile gets deleted or gets too old. """ + + def __init__(self, lockfile, interval): + threading.Thread.__init__(self) + self.daemon = True + self.lockfile, self.interval = lockfile, interval + #: Is one of 'reload', 'error' or 'exit' + self.status = None + + def run(self): + exists = os.path.exists + mtime = lambda p: os.stat(p).st_mtime + files = dict() + + for module in list(sys.modules.values()): + path = getattr(module, '__file__', '') or '' + if path[-4:] in ('.pyo', '.pyc'): path = path[:-1] + if path and exists(path): files[path] = mtime(path) + + while not self.status: + if not exists(self.lockfile)\ + or mtime(self.lockfile) < time.time() - self.interval - 5: + self.status = 'error' + thread.interrupt_main() + for path, lmtime in list(files.items()): + if not exists(path) or mtime(path) > lmtime: + self.status = 'reload' + thread.interrupt_main() + break + time.sleep(self.interval) + + def __enter__(self): + self.start() + + def __exit__(self, exc_type, *_): + if not self.status: self.status = 'exit' # silent exit + self.join() + return exc_type is not None and issubclass(exc_type, KeyboardInterrupt) + +############################################################################### +# Template Adapters ############################################################ +############################################################################### + + +class TemplateError(BottleException): + pass + + +class BaseTemplate(object): + """ Base class and minimal API for template adapters """ + extensions = ['tpl', 'html', 'thtml', 'stpl'] + settings = {} #used in prepare() + defaults = {} #used in render() + + def __init__(self, + source=None, + name=None, + lookup=None, + encoding='utf8', **settings): + """ Create a new template. + If the source parameter (str or buffer) is missing, the name argument + is used to guess a template filename. Subclasses can assume that + self.source and/or self.filename are set. Both are strings. + The lookup, encoding and settings parameters are stored as instance + variables. + The lookup parameter stores a list containing directory paths. + The encoding parameter should be used to decode byte strings or files. + The settings parameter contains a dict for engine-specific settings. + """ + self.name = name + self.source = source.read() if hasattr(source, 'read') else source + self.filename = source.filename if hasattr(source, 'filename') else None + self.lookup = [os.path.abspath(x) for x in lookup] if lookup else [] + self.encoding = encoding + self.settings = self.settings.copy() # Copy from class variable + self.settings.update(settings) # Apply + if not self.source and self.name: + self.filename = self.search(self.name, self.lookup) + if not self.filename: + raise TemplateError('Template %s not found.' % repr(name)) + if not self.source and not self.filename: + raise TemplateError('No template specified.') + self.prepare(**self.settings) + + @classmethod + def search(cls, name, lookup=None): + """ Search name in all directories specified in lookup. + First without, then with common extensions. Return first hit. """ + if not lookup: + raise depr(0, 12, "Empty template lookup path.", "Configure a template lookup path.") + + if os.path.isabs(name): + raise depr(0, 12, "Use of absolute path for template name.", + "Refer to templates with names or paths relative to the lookup path.") + + for spath in lookup: + spath = os.path.abspath(spath) + os.sep + fname = os.path.abspath(os.path.join(spath, name)) + if not fname.startswith(spath): continue + if os.path.isfile(fname): return fname + for ext in cls.extensions: + if os.path.isfile('%s.%s' % (fname, ext)): + return '%s.%s' % (fname, ext) + + @classmethod + def global_config(cls, key, *args): + """ This reads or sets the global settings stored in class.settings. """ + if args: + cls.settings = cls.settings.copy() # Make settings local to class + cls.settings[key] = args[0] + else: + return cls.settings[key] + + def prepare(self, **options): + """ Run preparations (parsing, caching, ...). + It should be possible to call this again to refresh a template or to + update settings. + """ + raise NotImplementedError + + def render(self, *args, **kwargs): + """ Render the template with the specified local variables and return + a single byte or unicode string. If it is a byte string, the encoding + must match self.encoding. This method must be thread-safe! + Local variables may be provided in dictionaries (args) + or directly, as keywords (kwargs). + """ + raise NotImplementedError + + +class MakoTemplate(BaseTemplate): + def prepare(self, **options): + from mako.template import Template + from mako.lookup import TemplateLookup + options.update({'input_encoding': self.encoding}) + options.setdefault('format_exceptions', bool(DEBUG)) + lookup = TemplateLookup(directories=self.lookup, **options) + if self.source: + self.tpl = Template(self.source, lookup=lookup, **options) + else: + self.tpl = Template(uri=self.name, + filename=self.filename, + lookup=lookup, **options) + + def render(self, *args, **kwargs): + for dictarg in args: + kwargs.update(dictarg) + _defaults = self.defaults.copy() + _defaults.update(kwargs) + return self.tpl.render(**_defaults) + + +class CheetahTemplate(BaseTemplate): + def prepare(self, **options): + from Cheetah.Template import Template + self.context = threading.local() + self.context.vars = {} + options['searchList'] = [self.context.vars] + if self.source: + self.tpl = Template(source=self.source, **options) + else: + self.tpl = Template(file=self.filename, **options) + + def render(self, *args, **kwargs): + for dictarg in args: + kwargs.update(dictarg) + self.context.vars.update(self.defaults) + self.context.vars.update(kwargs) + out = str(self.tpl) + self.context.vars.clear() + return out + + +class Jinja2Template(BaseTemplate): + def prepare(self, filters=None, tests=None, globals={}, **kwargs): + from jinja2 import Environment, FunctionLoader + self.env = Environment(loader=FunctionLoader(self.loader), **kwargs) + if filters: self.env.filters.update(filters) + if tests: self.env.tests.update(tests) + if globals: self.env.globals.update(globals) + if self.source: + self.tpl = self.env.from_string(self.source) + else: + self.tpl = self.env.get_template(self.name) + + def render(self, *args, **kwargs): + for dictarg in args: + kwargs.update(dictarg) + _defaults = self.defaults.copy() + _defaults.update(kwargs) + return self.tpl.render(**_defaults) + + def loader(self, name): + if name == self.filename: + fname = name + else: + fname = self.search(name, self.lookup) + if not fname: return + with open(fname, "rb") as f: + return (f.read().decode(self.encoding), fname, lambda: False) + + +class SimpleTemplate(BaseTemplate): + def prepare(self, + escape_func=html_escape, + noescape=False, + syntax=None, **ka): + self.cache = {} + enc = self.encoding + self._str = lambda x: touni(x, enc) + self._escape = lambda x: escape_func(touni(x, enc)) + self.syntax = syntax + if noescape: + self._str, self._escape = self._escape, self._str + + @cached_property + def co(self): + return compile(self.code, self.filename or '', 'exec') + + @cached_property + def code(self): + source = self.source + if not source: + with open(self.filename, 'rb') as f: + source = f.read() + try: + source, encoding = touni(source), 'utf8' + except UnicodeError: + raise depr(0, 11, 'Unsupported template encodings.', 'Use utf-8 for templates.') + parser = StplParser(source, encoding=encoding, syntax=self.syntax) + code = parser.translate() + self.encoding = parser.encoding + return code + + def _rebase(self, _env, _name=None, **kwargs): + _env['_rebase'] = (_name, kwargs) + + def _include(self, _env, _name=None, **kwargs): + env = _env.copy() + env.update(kwargs) + if _name not in self.cache: + self.cache[_name] = self.__class__(name=_name, lookup=self.lookup, syntax=self.syntax) + return self.cache[_name].execute(env['_stdout'], env) + + def execute(self, _stdout, kwargs): + env = self.defaults.copy() + env.update(kwargs) + env.update({ + '_stdout': _stdout, + '_printlist': _stdout.extend, + 'include': functools.partial(self._include, env), + 'rebase': functools.partial(self._rebase, env), + '_rebase': None, + '_str': self._str, + '_escape': self._escape, + 'get': env.get, + 'setdefault': env.setdefault, + 'defined': env.__contains__ + }) + exec(self.co, env) + if env.get('_rebase'): + subtpl, rargs = env.pop('_rebase') + rargs['base'] = ''.join(_stdout) #copy stdout + del _stdout[:] # clear stdout + return self._include(env, subtpl, **rargs) + return env + + def render(self, *args, **kwargs): + """ Render the template using keyword arguments as local variables. """ + env = {} + stdout = [] + for dictarg in args: + env.update(dictarg) + env.update(kwargs) + self.execute(stdout, env) + return ''.join(stdout) + + +class StplSyntaxError(TemplateError): + pass + + +class StplParser(object): + """ Parser for stpl templates. """ + _re_cache = {} #: Cache for compiled re patterns + + # This huge pile of voodoo magic splits python code into 8 different tokens. + # We use the verbose (?x) regex mode to make this more manageable + + _re_tok = r'''( + [urbURB]* + (?: ''(?!') + |""(?!") + |'{6} + |"{6} + |'(?:[^\\']|\\.)+?' + |"(?:[^\\"]|\\.)+?" + |'{3}(?:[^\\]|\\.|\n)+?'{3} + |"{3}(?:[^\\]|\\.|\n)+?"{3} + ) + )''' + + _re_inl = _re_tok.replace(r'|\n', '') # We re-use this string pattern later + + _re_tok += r''' + # 2: Comments (until end of line, but not the newline itself) + |(\#.*) + + # 3: Open and close (4) grouping tokens + |([\[\{\(]) + |([\]\}\)]) + + # 5,6: Keywords that start or continue a python block (only start of line) + |^([\ \t]*(?:if|for|while|with|try|def|class)\b) + |^([\ \t]*(?:elif|else|except|finally)\b) + + # 7: Our special 'end' keyword (but only if it stands alone) + |((?:^|;)[\ \t]*end[\ \t]*(?=(?:%(block_close)s[\ \t]*)?\r?$|;|\#)) + + # 8: A customizable end-of-code-block template token (only end of line) + |(%(block_close)s[\ \t]*(?=\r?$)) + + # 9: And finally, a single newline. The 10th token is 'everything else' + |(\r?\n) + ''' + + # Match the start tokens of code areas in a template + _re_split = r'''(?m)^[ \t]*(\\?)((%(line_start)s)|(%(block_start)s))''' + # Match inline statements (may contain python strings) + _re_inl = r'''%%(inline_start)s((?:%s|[^'"\n])*?)%%(inline_end)s''' % _re_inl + + # add the flag in front of the regexp to avoid Deprecation warning (see Issue #949) + # verbose and dot-matches-newline mode + _re_tok = '(?mx)' + _re_tok + _re_inl = '(?mx)' + _re_inl + + + default_syntax = '<% %> % {{ }}' + + def __init__(self, source, syntax=None, encoding='utf8'): + self.source, self.encoding = touni(source, encoding), encoding + self.set_syntax(syntax or self.default_syntax) + self.code_buffer, self.text_buffer = [], [] + self.lineno, self.offset = 1, 0 + self.indent, self.indent_mod = 0, 0 + self.paren_depth = 0 + + def get_syntax(self): + """ Tokens as a space separated string (default: <% %> % {{ }}) """ + return self._syntax + + def set_syntax(self, syntax): + self._syntax = syntax + self._tokens = syntax.split() + if syntax not in self._re_cache: + names = 'block_start block_close line_start inline_start inline_end' + etokens = map(re.escape, self._tokens) + pattern_vars = dict(zip(names.split(), etokens)) + patterns = (self._re_split, self._re_tok, self._re_inl) + patterns = [re.compile(p % pattern_vars) for p in patterns] + self._re_cache[syntax] = patterns + self.re_split, self.re_tok, self.re_inl = self._re_cache[syntax] + + syntax = property(get_syntax, set_syntax) + + def translate(self): + if self.offset: raise RuntimeError('Parser is a one time instance.') + while True: + m = self.re_split.search(self.source, pos=self.offset) + if m: + text = self.source[self.offset:m.start()] + self.text_buffer.append(text) + self.offset = m.end() + if m.group(1): # Escape syntax + line, sep, _ = self.source[self.offset:].partition('\n') + self.text_buffer.append(self.source[m.start():m.start(1)] + + m.group(2) + line + sep) + self.offset += len(line + sep) + continue + self.flush_text() + self.offset += self.read_code(self.source[self.offset:], + multiline=bool(m.group(4))) + else: + break + self.text_buffer.append(self.source[self.offset:]) + self.flush_text() + return ''.join(self.code_buffer) + + def read_code(self, pysource, multiline): + code_line, comment = '', '' + offset = 0 + while True: + m = self.re_tok.search(pysource, pos=offset) + if not m: + code_line += pysource[offset:] + offset = len(pysource) + self.write_code(code_line.strip(), comment) + break + code_line += pysource[offset:m.start()] + offset = m.end() + _str, _com, _po, _pc, _blk1, _blk2, _end, _cend, _nl = m.groups() + if self.paren_depth > 0 and (_blk1 or _blk2): # a if b else c + code_line += _blk1 or _blk2 + continue + if _str: # Python string + code_line += _str + elif _com: # Python comment (up to EOL) + comment = _com + if multiline and _com.strip().endswith(self._tokens[1]): + multiline = False # Allow end-of-block in comments + elif _po: # open parenthesis + self.paren_depth += 1 + code_line += _po + elif _pc: # close parenthesis + if self.paren_depth > 0: + # we could check for matching parentheses here, but it's + # easier to leave that to python - just check counts + self.paren_depth -= 1 + code_line += _pc + elif _blk1: # Start-block keyword (if/for/while/def/try/...) + code_line = _blk1 + self.indent += 1 + self.indent_mod -= 1 + elif _blk2: # Continue-block keyword (else/elif/except/...) + code_line = _blk2 + self.indent_mod -= 1 + elif _cend: # The end-code-block template token (usually '%>') + if multiline: multiline = False + else: code_line += _cend + elif _end: + self.indent -= 1 + self.indent_mod += 1 + else: # \n + self.write_code(code_line.strip(), comment) + self.lineno += 1 + code_line, comment, self.indent_mod = '', '', 0 + if not multiline: + break + + return offset + + def flush_text(self): + text = ''.join(self.text_buffer) + del self.text_buffer[:] + if not text: return + parts, pos, nl = [], 0, '\\\n' + ' ' * self.indent + for m in self.re_inl.finditer(text): + prefix, pos = text[pos:m.start()], m.end() + if prefix: + parts.append(nl.join(map(repr, prefix.splitlines(True)))) + if prefix.endswith('\n'): parts[-1] += nl + parts.append(self.process_inline(m.group(1).strip())) + if pos < len(text): + prefix = text[pos:] + lines = prefix.splitlines(True) + if lines[-1].endswith('\\\\\n'): lines[-1] = lines[-1][:-3] + elif lines[-1].endswith('\\\\\r\n'): lines[-1] = lines[-1][:-4] + parts.append(nl.join(map(repr, lines))) + code = '_printlist((%s,))' % ', '.join(parts) + self.lineno += code.count('\n') + 1 + self.write_code(code) + + @staticmethod + def process_inline(chunk): + if chunk[0] == '!': return '_str(%s)' % chunk[1:] + return '_escape(%s)' % chunk + + def write_code(self, line, comment=''): + code = ' ' * (self.indent + self.indent_mod) + code += line.lstrip() + comment + '\n' + self.code_buffer.append(code) + + +def template(*args, **kwargs): + """ + Get a rendered template as a string iterator. + You can use a name, a filename or a template string as first parameter. + Template rendering arguments can be passed as dictionaries + or directly (as keyword arguments). + """ + tpl = args[0] if args else None + for dictarg in args[1:]: + kwargs.update(dictarg) + adapter = kwargs.pop('template_adapter', SimpleTemplate) + lookup = kwargs.pop('template_lookup', TEMPLATE_PATH) + tplid = (id(lookup), tpl) + if tplid not in TEMPLATES or DEBUG: + settings = kwargs.pop('template_settings', {}) + if isinstance(tpl, adapter): + TEMPLATES[tplid] = tpl + if settings: TEMPLATES[tplid].prepare(**settings) + elif "\n" in tpl or "{" in tpl or "%" in tpl or '$' in tpl: + TEMPLATES[tplid] = adapter(source=tpl, lookup=lookup, **settings) + else: + TEMPLATES[tplid] = adapter(name=tpl, lookup=lookup, **settings) + if not TEMPLATES[tplid]: + abort(500, 'Template (%s) not found' % tpl) + return TEMPLATES[tplid].render(kwargs) + + +mako_template = functools.partial(template, template_adapter=MakoTemplate) +cheetah_template = functools.partial(template, + template_adapter=CheetahTemplate) +jinja2_template = functools.partial(template, template_adapter=Jinja2Template) + + +def view(tpl_name, **defaults): + """ Decorator: renders a template for a handler. + The handler can control its behavior like that: + + - return a dict of template vars to fill out the template + - return something other than a dict and the view decorator will not + process the template, but return the handler result as is. + This includes returning a HTTPResponse(dict) to get, + for instance, JSON with autojson or other castfilters. + """ + + def decorator(func): + + @functools.wraps(func) + def wrapper(*args, **kwargs): + result = func(*args, **kwargs) + if isinstance(result, (dict, DictMixin)): + tplvars = defaults.copy() + tplvars.update(result) + return template(tpl_name, **tplvars) + elif result is None: + return template(tpl_name, **defaults) + return result + + return wrapper + + return decorator + + +mako_view = functools.partial(view, template_adapter=MakoTemplate) +cheetah_view = functools.partial(view, template_adapter=CheetahTemplate) +jinja2_view = functools.partial(view, template_adapter=Jinja2Template) + +############################################################################### +# Constants and Globals ######################################################## +############################################################################### + +TEMPLATE_PATH = ['./', './views/'] +TEMPLATES = {} +DEBUG = False +NORUN = False # If set, run() does nothing. Used by load_app() + +#: A dict to map HTTP status codes (e.g. 404) to phrases (e.g. 'Not Found') +HTTP_CODES = httplib.responses.copy() +HTTP_CODES[418] = "I'm a teapot" # RFC 2324 +HTTP_CODES[428] = "Precondition Required" +HTTP_CODES[429] = "Too Many Requests" +HTTP_CODES[431] = "Request Header Fields Too Large" +HTTP_CODES[451] = "Unavailable For Legal Reasons" # RFC 7725 +HTTP_CODES[511] = "Network Authentication Required" +_HTTP_STATUS_LINES = dict((k, '%d %s' % (k, v)) + for (k, v) in HTTP_CODES.items()) + +#: The default template used for error pages. Override with @error() +ERROR_PAGE_TEMPLATE = """ +%%try: + %%from %s import DEBUG, request + + + + Error: {{e.status}} + + + +

Error: {{e.status}}

+

Sorry, the requested URL {{repr(request.url)}} + caused an error:

+
{{e.body}}
+ %%if DEBUG and e.exception: +

Exception:

+ %%try: + %%exc = repr(e.exception) + %%except: + %%exc = '' %% type(e.exception).__name__ + %%end +
{{exc}}
+ %%end + %%if DEBUG and e.traceback: +

Traceback:

+
{{e.traceback}}
+ %%end + + +%%except ImportError: + ImportError: Could not generate the error page. Please add bottle to + the import path. +%%end +""" % __name__ + +#: A thread-safe instance of :class:`LocalRequest`. If accessed from within a +#: request callback, this instance always refers to the *current* request +#: (even on a multi-threaded server). +request = LocalRequest() + +#: A thread-safe instance of :class:`LocalResponse`. It is used to change the +#: HTTP response for the *current* request. +response = LocalResponse() + +#: A thread-safe namespace. Not used by Bottle. +local = threading.local() + +# Initialize app stack (create first empty Bottle app now deferred until needed) +# BC: 0.6.4 and needed for run() +apps = app = default_app = AppStack() + +#: A virtual package that redirects import statements. +#: Example: ``import bottle.ext.sqlite`` actually imports `bottle_sqlite`. +ext = _ImportRedirect('bottle.ext' if __name__ == '__main__' else + __name__ + ".ext", 'bottle_%s').module + + +def _main(argv): # pragma: no coverage + args, parser = _cli_parse(argv) + + def _cli_error(cli_msg): + parser.print_help() + _stderr('\nError: %s\n' % cli_msg) + sys.exit(1) + + if args.version: + print('Bottle %s' % __version__) + sys.exit(0) + if not args.app: + _cli_error("No application entry point specified.") + + sys.path.insert(0, '.') + sys.modules.setdefault('bottle', sys.modules['__main__']) + + host, port = (args.bind or 'localhost'), 8080 + if ':' in host and host.rfind(']') < host.rfind(':'): + host, port = host.rsplit(':', 1) + host = host.strip('[]') + + config = ConfigDict() + + for cfile in args.conf or []: + try: + if cfile.endswith('.json'): + with open(cfile, 'rb') as fp: + config.load_dict(json_loads(fp.read())) + else: + config.load_config(cfile) + except configparser.Error as parse_error: + _cli_error(parse_error) + except IOError: + _cli_error("Unable to read config file %r" % cfile) + except (UnicodeError, TypeError, ValueError) as error: + _cli_error("Unable to parse config file %r: %s" % (cfile, error)) + + for cval in args.param or []: + if '=' in cval: + config.update((cval.split('=', 1),)) + else: + config[cval] = True + + run(args.app, + host=host, + port=int(port), + server=args.server, + reloader=args.reload, + plugins=args.plugin, + debug=args.debug, + config=config) + + +if __name__ == '__main__': # pragma: no coverage + _main(sys.argv) diff --git a/resources/lib/deps/simplecache.py b/resources/lib/deps/simplecache.py new file mode 100644 index 0000000..448b5f9 --- /dev/null +++ b/resources/lib/deps/simplecache.py @@ -0,0 +1,307 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +'''provides a simple stateless caching system for Kodi addons and plugins''' + +import sys +import xbmcvfs +import xbmcgui +import xbmc +import xbmcaddon +import datetime +import time +import sqlite3 +import json +from functools import reduce + +class SimpleCache(object): + '''simple stateless caching system for Kodi''' + enable_mem_cache = True + data_is_json = False + global_checksum = None + _exit = False + _auto_clean_interval = datetime.timedelta(hours=4) + _win = None + _busy_tasks = [] + _database = None + + def __init__(self, addon_id): + '''Initialize our caching class''' + self.addon_id = addon_id + self._win = xbmcgui.Window(10000) + self._monitor = xbmc.Monitor() + self.check_cleanup() + self._log_msg("Initialized") + + def close(self): + '''tell any tasks to stop immediately (as we can be called multithreaded) and cleanup objects''' + self._exit = True + # wait for all tasks to complete + while self._busy_tasks and not self._monitor.abortRequested(): + xbmc.sleep(25) + del self._win + del self._monitor + self._log_msg("Closed") + + def __del__(self): + '''make sure close is called''' + if not self._exit: + self.close() + + def get(self, endpoint, checksum="", json_data=False): + ''' + get object from cache and return the results + endpoint: the (unique) name of the cache object as reference + checkum: optional argument to check if the checksum in the cacheobject matches the checkum provided + ''' + checksum = self._get_checksum(checksum) + cur_time = self._get_timestamp(datetime.datetime.now()) + result = None + # 1: try memory cache first + if self.enable_mem_cache: + result = self._get_mem_cache(endpoint, checksum, cur_time, json_data) + + # 2: fallback to _database cache + if result is None: + result = self._get_db_cache(endpoint, checksum, cur_time, json_data) + + return result + + def set(self, endpoint, data, checksum="", expiration=datetime.timedelta(days=30), json_data=False): + ''' + set data in cache + ''' + task_name = "set.%s" % endpoint + self._busy_tasks.append(task_name) + checksum = self._get_checksum(checksum) + expires = self._get_timestamp(datetime.datetime.now() + expiration) + + # memory cache: write to window property + if self.enable_mem_cache and not self._exit: + self._set_mem_cache(endpoint, checksum, expires, data, json_data) + + # db cache + if not self._exit: + self._set_db_cache(endpoint, checksum, expires, data, json_data) + + # remove this task from list + self._busy_tasks.remove(task_name) + + def check_cleanup(self): + '''check if cleanup is needed - public method, may be called by calling addon''' + cur_time = datetime.datetime.now() + lastexecuted = self._win.getProperty("simplecache.clean.lastexecuted") + if not lastexecuted: + self._win.setProperty("simplecache.clean.lastexecuted", repr(cur_time)) + elif (eval(lastexecuted) + self._auto_clean_interval) < cur_time: + # cleanup needed... + self._do_cleanup() + + def _get_mem_cache(self, endpoint, checksum, cur_time, json_data): + ''' + get cache data from memory cache + we use window properties because we need to be stateless + ''' + result = None + cachedata = self._win.getProperty(endpoint) + + if cachedata: + if json_data or self.data_is_json: + cachedata = json.loads(cachedata) + else: + cachedata = eval(cachedata) + if cachedata[0] > cur_time: + if not checksum or checksum == cachedata[2]: + result = cachedata[1] + return result + + def _set_mem_cache(self, endpoint, checksum, expires, data, json_data): + ''' + window property cache as alternative for memory cache + usefull for (stateless) plugins + ''' + cachedata = (expires, data, checksum) + if json_data or self.data_is_json: + cachedata_str = json.dumps(cachedata) + else: + cachedata_str = repr(cachedata) + self._win.setProperty(endpoint, cachedata_str) + + + def _get_db_cache(self, endpoint, checksum, cur_time, json_data): + '''get cache data from sqllite _database''' + result = None + query = "SELECT expires, data, checksum FROM simplecache WHERE id = ?" + cache_data = self._execute_sql(query, (endpoint,)) + if cache_data: + cache_data = cache_data.fetchone() + if cache_data and cache_data[0] > cur_time: + if not checksum or cache_data[2] == checksum: + if json_data or self.data_is_json: + result = json.loads(cache_data[1]) + else: + result = eval(cache_data[1]) + # also set result in memory cache for further access + if self.enable_mem_cache: + self._set_mem_cache(endpoint, checksum, cache_data[0], result, json_data) + return result + + def _set_db_cache(self, endpoint, checksum, expires, data, json_data): + ''' store cache data in _database ''' + query = "INSERT OR REPLACE INTO simplecache( id, expires, data, checksum) VALUES (?, ?, ?, ?)" + if json_data or self.data_is_json: + data = json.dumps(data) + else: + data = repr(data) + self._execute_sql(query, (endpoint, expires, data, checksum)) + + def _do_cleanup(self): + '''perform cleanup task''' + if self._exit or self._monitor.abortRequested(): + return + self._busy_tasks.append(__name__) + cur_time = datetime.datetime.now() + cur_timestamp = self._get_timestamp(cur_time) + self._log_msg("Running cleanup...") + if self._win.getProperty("simplecachecleanbusy"): + return + self._win.setProperty("simplecachecleanbusy", "busy") + + query = "SELECT id, expires FROM simplecache" + for cache_data in self._execute_sql(query).fetchall(): + cache_id = cache_data[0] + cache_expires = cache_data[1] + + if self._exit or self._monitor.abortRequested(): + return + + # always cleanup all memory objects on each interval + self._win.clearProperty(cache_id) + + # clean up db cache object only if expired + if cache_expires < cur_timestamp: + query = 'DELETE FROM simplecache WHERE id = ?' + self._execute_sql(query, (cache_id,)) + self._log_msg("delete from db %s" % cache_id) + + # compact db + self._execute_sql("VACUUM") + + # remove task from list + self._busy_tasks.remove(__name__) + self._win.setProperty("simplecache.clean.lastexecuted", repr(cur_time)) + self._win.clearProperty("simplecachecleanbusy") + self._log_msg("Auto cleanup done") + + def _get_database(self): + '''get reference to our sqllite _database - performs basic integrity check''' + addon = xbmcaddon.Addon(self.addon_id) + dbpath = addon.getAddonInfo('profile') + dbfile = xbmcvfs.translatePath("%s/simplecache.db" % dbpath) + + if not xbmcvfs.exists(dbpath): + xbmcvfs.mkdirs(dbpath) + del addon + try: + connection = sqlite3.connect(dbfile, timeout=30, isolation_level=None) + connection.execute('SELECT * FROM simplecache LIMIT 1') + return connection + except Exception as error: + # our _database is corrupt or doesn't exist yet, we simply try to recreate it + if xbmcvfs.exists(dbfile): + xbmcvfs.delete(dbfile) + try: + connection = sqlite3.connect(dbfile, timeout=30, isolation_level=None) + connection.execute( + """CREATE TABLE IF NOT EXISTS simplecache( + id TEXT UNIQUE, expires INTEGER, data TEXT, checksum INTEGER)""") + return connection + except Exception as error: + self._log_msg("Exception while initializing _database: %s" % str(error), xbmc.LOGWARNING) + self.close() + return None + + def _execute_sql(self, query, data=None): + '''little wrapper around execute and executemany to just retry a db command if db is locked''' + retries = 0 + result = None + error = None + # always use new db object because we need to be sure that data is available for other simplecache instances + with self._get_database() as _database: + while not retries == 10 and not self._monitor.abortRequested(): + if self._exit: + return None + try: + if isinstance(data, list): + result = _database.executemany(query, data) + elif data: + result = _database.execute(query, data) + else: + result = _database.execute(query) + return result + except sqlite3.OperationalError as error: + if "_database is locked" in error: + self._log_msg("retrying DB commit...") + retries += 1 + self._monitor.waitForAbort(0.5) + else: + break + except Exception as error: + break + self._log_msg("_database ERROR ! -- %s" % str(error), xbmc.LOGWARNING) + return None + + @staticmethod + def _log_msg(msg, loglevel=xbmc.LOGDEBUG): + '''helper to send a message to the kodi log''' + xbmc.log("Skin Helper Simplecache --> %s" % msg, level=loglevel) + + @staticmethod + def _get_timestamp(date_time): + '''Converts a datetime object to unix timestamp''' + return int(time.mktime(date_time.timetuple())) + + def _get_checksum(self, stringinput): + '''get int checksum from string''' + if not stringinput and not self.global_checksum: + return 0 + if self.global_checksum: + stringinput = "%s-%s" %(self.global_checksum, stringinput) + else: + stringinput = str(stringinput) + return reduce(lambda x, y: x + y, map(ord, stringinput)) + + +def use_cache(cache_days=14): + ''' + wrapper around our simple cache to use as decorator + Usage: define an instance of SimpleCache with name "cache" (self.cache) in your class + Any method that needs caching just add @use_cache as decorator + NOTE: use unnamed arguments for calling the method and named arguments for optional settings + ''' + def decorator(func): + '''our decorator''' + def decorated(*args, **kwargs): + '''process the original method and apply caching of the results''' + method_class = args[0] + method_class_name = method_class.__class__.__name__ + cache_str = "%s.%s" % (method_class_name, func.__name__) + # cache identifier is based on positional args only + # named args are considered optional and ignored + for item in args[1:]: + cache_str += u".%s" % item + cache_str = cache_str.lower() + cachedata = method_class.cache.get(cache_str) + global_cache_ignore = False + try: + global_cache_ignore = method_class.ignore_cache + except Exception: + pass + if cachedata is not None and not kwargs.get("ignore_cache", False) and not global_cache_ignore: + return cachedata + else: + result = func(*args, **kwargs) + method_class.cache.set(cache_str, result, expiration=datetime.timedelta(days=cache_days)) + return result + return decorated + return decorator diff --git a/resources/lib/deps/spotipy/__init__.py b/resources/lib/deps/spotipy/__init__.py new file mode 100644 index 0000000..7f3d859 --- /dev/null +++ b/resources/lib/deps/spotipy/__init__.py @@ -0,0 +1,5 @@ +from .cache_handler import * # noqa +from .client import * # noqa +from .exceptions import * # noqa +from .oauth2 import * # noqa +from .util import * # noqa diff --git a/resources/lib/deps/spotipy/cache_handler.py b/resources/lib/deps/spotipy/cache_handler.py new file mode 100644 index 0000000..3917c4f --- /dev/null +++ b/resources/lib/deps/spotipy/cache_handler.py @@ -0,0 +1,173 @@ +__all__ = [ + 'CacheHandler', + 'CacheFileHandler', + 'DjangoSessionCacheHandler', + 'FlaskSessionCacheHandler', + 'MemoryCacheHandler'] + +import errno +import json +import logging +import os +from spotipy.util import CLIENT_CREDS_ENV_VARS + +logger = logging.getLogger(__name__) + + +class CacheHandler(): + """ + An abstraction layer for handling the caching and retrieval of + authorization tokens. + + Custom extensions of this class must implement get_cached_token + and save_token_to_cache methods with the same input and output + structure as the CacheHandler class. + """ + + def get_cached_token(self): + """ + Get and return a token_info dictionary object. + """ + # return token_info + raise NotImplementedError() + + def save_token_to_cache(self, token_info): + """ + Save a token_info dictionary object to the cache and return None. + """ + raise NotImplementedError() + return None + + +class CacheFileHandler(CacheHandler): + """ + Handles reading and writing cached Spotify authorization tokens + as json files on disk. + """ + + def __init__(self, + cache_path=None, + username=None, + encoder_cls=None): + """ + Parameters: + * cache_path: May be supplied, will otherwise be generated + (takes precedence over `username`) + * username: May be supplied or set as environment variable + (will set `cache_path` to `.cache-{username}`) + * encoder_cls: May be supplied as a means of overwriting the + default serializer used for writing tokens to disk + """ + self.encoder_cls = encoder_cls + if cache_path: + self.cache_path = cache_path + else: + cache_path = ".cache" + username = (username or os.getenv(CLIENT_CREDS_ENV_VARS["client_username"])) + if username: + cache_path += "-" + str(username) + self.cache_path = cache_path + + def get_cached_token(self): + token_info = None + + try: + f = open(self.cache_path) + token_info_string = f.read() + f.close() + token_info = json.loads(token_info_string) + + except IOError as error: + if error.errno == errno.ENOENT: + logger.debug("cache does not exist at: %s", self.cache_path) + else: + logger.warning("Couldn't read cache at: %s", self.cache_path) + + return token_info + + def save_token_to_cache(self, token_info): + try: + f = open(self.cache_path, "w") + f.write(json.dumps(token_info, cls=self.encoder_cls)) + f.close() + except IOError: + logger.warning('Couldn\'t write token to cache at: %s', + self.cache_path) + + +class MemoryCacheHandler(CacheHandler): + """ + A cache handler that simply stores the token info in memory as an + instance attribute of this class. The token info will be lost when this + instance is freed. + """ + + def __init__(self, token_info=None): + """ + Parameters: + * token_info: The token info to store in memory. Can be None. + """ + self.token_info = token_info + + def get_cached_token(self): + return self.token_info + + def save_token_to_cache(self, token_info): + self.token_info = token_info + + +class DjangoSessionCacheHandler(CacheHandler): + """ + A cache handler that stores the token info in the session framework + provided by Django. + + Read more at https://docs.djangoproject.com/en/3.2/topics/http/sessions/ + """ + + def __init__(self, request): + """ + Parameters: + * request: HttpRequest object provided by Django for every + incoming request + """ + self.request = request + + def get_cached_token(self): + token_info = None + try: + token_info = self.request.session['token_info'] + except KeyError: + logger.debug("Token not found in the session") + + return token_info + + def save_token_to_cache(self, token_info): + try: + self.request.session['token_info'] = token_info + except Exception as e: + logger.warning("Error saving token to cache: " + str(e)) + + +class FlaskSessionCacheHandler(CacheHandler): + """ + A cache handler that stores the token info in the session framework + provided by flask. + """ + + def __init__(self, session): + self.session = session + + def get_cached_token(self): + token_info = None + try: + token_info = self.session["token_info"] + except KeyError: + logger.debug("Token not found in the session") + + return token_info + + def save_token_to_cache(self, token_info): + try: + self.session["token_info"] = token_info + except Exception as e: + logger.warning("Error saving token to cache: " + str(e)) diff --git a/resources/lib/deps/spotipy/client.py b/resources/lib/deps/spotipy/client.py new file mode 100644 index 0000000..d3b918f --- /dev/null +++ b/resources/lib/deps/spotipy/client.py @@ -0,0 +1,2035 @@ +# -*- coding: utf-8 -*- + +""" A simple and thin Python library for the Spotify Web API """ + +__all__ = ["Spotify", "SpotifyException"] + +import json +import logging +import re +import warnings + +import requests +import six +import urllib3 + +from spotipy.exceptions import SpotifyException + +from collections import defaultdict + +logger = logging.getLogger(__name__) + + +class Spotify(object): + """ + Example usage:: + + import spotipy + + urn = 'spotify:artist:3jOstUTkEu2JkjvRdBA5Gu' + sp = spotipy.Spotify() + + artist = sp.artist(urn) + print(artist) + + user = sp.user('plamere') + print(user) + """ + max_retries = 3 + default_retry_codes = (429, 500, 502, 503, 504) + country_codes = [ + "AD", + "AR", + "AU", + "AT", + "BE", + "BO", + "BR", + "BG", + "CA", + "CL", + "CO", + "CR", + "CY", + "CZ", + "DK", + "DO", + "EC", + "SV", + "EE", + "FI", + "FR", + "DE", + "GR", + "GT", + "HN", + "HK", + "HU", + "IS", + "ID", + "IE", + "IT", + "JP", + "LV", + "LI", + "LT", + "LU", + "MY", + "MT", + "MX", + "MC", + "NL", + "NZ", + "NI", + "NO", + "PA", + "PY", + "PE", + "PH", + "PL", + "PT", + "SG", + "ES", + "SK", + "SE", + "CH", + "TW", + "TR", + "GB", + "US", + "UY"] + + # Spotify URI scheme defined in [1], and the ID format as base-62 in [2]. + # + # Unfortunately the IANA specification is out of date and doesn't include the new types + # show and episode. Additionally, for the user URI, it does not specify which characters + # are valid for usernames, so the assumption is alphanumeric which coincidentially are also + # the same ones base-62 uses. + # In limited manual exploration this seems to hold true, as newly accounts are assigned an + # identifier that looks like the base-62 of all other IDs, but some older accounts only have + # numbers and even older ones seemed to have been allowed to freely pick this name. + # + # [1] https://www.iana.org/assignments/uri-schemes/prov/spotify + # [2] https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids + _regex_spotify_uri = r'^spotify:(?:(?Ptrack|artist|album|playlist|show|episode):(?P[0-9A-Za-z]+)|user:(?P[0-9A-Za-z]+):playlist:(?P[0-9A-Za-z]+))$' # noqa: E501 + + # Spotify URLs are defined at [1]. The assumption is made that they are all + # pointing to open.spotify.com, so a regex is used to parse them as well, + # instead of a more complex URL parsing function. + # + # [1] https://developer.spotify.com/documentation/web-api/#spotify-uris-and-ids + _regex_spotify_url = r'^(http[s]?:\/\/)?open.spotify.com\/(?Ptrack|artist|album|playlist|show|episode|user)\/(?P[0-9A-Za-z]+)(\?.*)?$' # noqa: E501 + + _regex_base62 = r'^[0-9A-Za-z]+$' + + def __init__( + self, + auth=None, + requests_session=True, + client_credentials_manager=None, + oauth_manager=None, + auth_manager=None, + proxies=None, + requests_timeout=5, + status_forcelist=None, + retries=max_retries, + status_retries=max_retries, + backoff_factor=0.3, + language=None, + ): + """ + Creates a Spotify API client. + + :param auth: An access token (optional) + :param requests_session: + A Requests session object or a truthy value to create one. + A falsy value disables sessions. + It should generally be a good idea to keep sessions enabled + for performance reasons (connection pooling). + :param client_credentials_manager: + SpotifyClientCredentials object + :param oauth_manager: + SpotifyOAuth object + :param auth_manager: + SpotifyOauth, SpotifyClientCredentials, + or SpotifyImplicitGrant object + :param proxies: + Definition of proxies (optional). + See Requests doc https://2.python-requests.org/en/master/user/advanced/#proxies + :param requests_timeout: + Tell Requests to stop waiting for a response after a given + number of seconds + :param status_forcelist: + Tell requests what type of status codes retries should occur on + :param retries: + Total number of retries to allow + :param status_retries: + Number of times to retry on bad status codes + :param backoff_factor: + A backoff factor to apply between attempts after the second try + See urllib3 https://urllib3.readthedocs.io/en/latest/reference/urllib3.util.html + :param language: + The language parameter advertises what language the user prefers to see. + See ISO-639-1 language code: https://en.wikipedia.org/wiki/List_of_ISO_639-1_codes + """ + self.prefix = "https://api.spotify.com/v1/" + self._auth = auth + self.client_credentials_manager = client_credentials_manager + self.oauth_manager = oauth_manager + self.auth_manager = auth_manager + self.proxies = proxies + self.requests_timeout = requests_timeout + self.status_forcelist = status_forcelist or self.default_retry_codes + self.backoff_factor = backoff_factor + self.retries = retries + self.status_retries = status_retries + self.language = language + + if isinstance(requests_session, requests.Session): + self._session = requests_session + else: + if requests_session: # Build a new session. + self._build_session() + else: # Use the Requests API module as a "session". + self._session = requests.api + + def set_auth(self, auth): + self._auth = auth + + @property + def auth_manager(self): + return self._auth_manager + + @auth_manager.setter + def auth_manager(self, auth_manager): + if auth_manager is not None: + self._auth_manager = auth_manager + else: + self._auth_manager = ( + self.client_credentials_manager or self.oauth_manager + ) + + def __del__(self): + """Make sure the connection (pool) gets closed""" + if isinstance(self._session, requests.Session): + self._session.close() + + def _build_session(self): + self._session = requests.Session() + retry = urllib3.Retry( + total=self.retries, + connect=None, + read=False, + allowed_methods=frozenset(['GET', 'POST', 'PUT', 'DELETE']), + status=self.status_retries, + backoff_factor=self.backoff_factor, + status_forcelist=self.status_forcelist) + + adapter = requests.adapters.HTTPAdapter(max_retries=retry) + self._session.mount('http://', adapter) + self._session.mount('https://', adapter) + + def _auth_headers(self): + if self._auth: + return {"Authorization": "Bearer {0}".format(self._auth)} + if not self.auth_manager: + return {} + try: + token = self.auth_manager.get_access_token(as_dict=False) + except TypeError: + token = self.auth_manager.get_access_token() + return {"Authorization": "Bearer {0}".format(token)} + + def _internal_call(self, method, url, payload, params): + args = dict(params=params) + if not url.startswith("http"): + url = self.prefix + url + headers = self._auth_headers() + + if "content_type" in args["params"]: + headers["Content-Type"] = args["params"]["content_type"] + del args["params"]["content_type"] + if payload: + args["data"] = payload + else: + headers["Content-Type"] = "application/json" + if payload: + args["data"] = json.dumps(payload) + + if self.language is not None: + headers["Accept-Language"] = self.language + + logger.debug('Sending %s to %s with Params: %s Headers: %s and Body: %r ', + method, url, args.get("params"), headers, args.get('data')) + + try: + response = self._session.request( + method, url, headers=headers, proxies=self.proxies, + timeout=self.requests_timeout, **args + ) + + response.raise_for_status() + results = response.json() + except requests.exceptions.HTTPError as http_error: + response = http_error.response + try: + json_response = response.json() + error = json_response.get("error", {}) + msg = error.get("message") + reason = error.get("reason") + except ValueError: + # if the response cannot be decoded into JSON (which raises a ValueError), + # then try to decode it into text + + # if we receive an empty string (which is falsy), then replace it with `None` + msg = response.text or None + reason = None + + logger.error( + 'HTTP Error for %s to %s with Params: %s returned %s due to %s', + method, url, args.get("params"), response.status_code, msg + ) + + raise SpotifyException( + response.status_code, + -1, + "%s:\n %s" % (response.url, msg), + reason=reason, + headers=response.headers, + ) + except requests.exceptions.RetryError as retry_error: + request = retry_error.request + logger.error('Max Retries reached') + try: + reason = retry_error.args[0].reason + except (IndexError, AttributeError): + reason = None + raise SpotifyException( + 429, + -1, + "%s:\n %s" % (request.path_url, "Max Retries"), + reason=reason + ) + except ValueError: + results = None + + logger.debug('RESULTS: %s', results) + return results + + def _get(self, url, args=None, payload=None, **kwargs): + if args: + kwargs.update(args) + + return self._internal_call("GET", url, payload, kwargs) + + def _post(self, url, args=None, payload=None, **kwargs): + if args: + kwargs.update(args) + return self._internal_call("POST", url, payload, kwargs) + + def _delete(self, url, args=None, payload=None, **kwargs): + if args: + kwargs.update(args) + return self._internal_call("DELETE", url, payload, kwargs) + + def _put(self, url, args=None, payload=None, **kwargs): + if args: + kwargs.update(args) + return self._internal_call("PUT", url, payload, kwargs) + + def next(self, result): + """ returns the next result given a paged result + + Parameters: + - result - a previously returned paged result + """ + if result["next"]: + return self._get(result["next"]) + else: + return None + + def previous(self, result): + """ returns the previous result given a paged result + + Parameters: + - result - a previously returned paged result + """ + if result["previous"]: + return self._get(result["previous"]) + else: + return None + + def track(self, track_id, market=None): + """ returns a single track given the track's ID, URI or URL + + Parameters: + - track_id - a spotify URI, URL or ID + - market - an ISO 3166-1 alpha-2 country code. + """ + + trid = self._get_id("track", track_id) + return self._get("tracks/" + trid, market=market) + + def tracks(self, tracks, market=None): + """ returns a list of tracks given a list of track IDs, URIs, or URLs + + Parameters: + - tracks - a list of spotify URIs, URLs or IDs. Maximum: 50 IDs. + - market - an ISO 3166-1 alpha-2 country code. + """ + + tlist = [self._get_id("track", t) for t in tracks] + return self._get("tracks/?ids=" + ",".join(tlist), market=market) + + def artist(self, artist_id): + """ returns a single artist given the artist's ID, URI or URL + + Parameters: + - artist_id - an artist ID, URI or URL + """ + + trid = self._get_id("artist", artist_id) + return self._get("artists/" + trid) + + def artists(self, artists): + """ returns a list of artists given the artist IDs, URIs, or URLs + + Parameters: + - artists - a list of artist IDs, URIs or URLs + """ + + tlist = [self._get_id("artist", a) for a in artists] + return self._get("artists/?ids=" + ",".join(tlist)) + + def artist_albums( + self, artist_id, album_type=None, country=None, limit=20, offset=0 + ): + """ Get Spotify catalog information about an artist's albums + + Parameters: + - artist_id - the artist ID, URI or URL + - album_type - 'album', 'single', 'appears_on', 'compilation' + - country - limit the response to one particular country. + - limit - the number of albums to return + - offset - the index of the first album to return + """ + + trid = self._get_id("artist", artist_id) + return self._get( + "artists/" + trid + "/albums", + album_type=album_type, + country=country, + limit=limit, + offset=offset, + ) + + def artist_top_tracks(self, artist_id, country="US"): + """ Get Spotify catalog information about an artist's top 10 tracks + by country. + + Parameters: + - artist_id - the artist ID, URI or URL + - country - limit the response to one particular country. + """ + + trid = self._get_id("artist", artist_id) + return self._get("artists/" + trid + "/top-tracks", country=country) + + def artist_related_artists(self, artist_id): + """ Get Spotify catalog information about artists similar to an + identified artist. Similarity is based on analysis of the + Spotify community's listening history. + + Parameters: + - artist_id - the artist ID, URI or URL + """ + trid = self._get_id("artist", artist_id) + return self._get("artists/" + trid + "/related-artists") + + def album(self, album_id, market=None): + """ returns a single album given the album's ID, URIs or URL + + Parameters: + - album_id - the album ID, URI or URL + - market - an ISO 3166-1 alpha-2 country code + """ + + trid = self._get_id("album", album_id) + if market is not None: + return self._get("albums/" + trid + '?market=' + market) + else: + return self._get("albums/" + trid) + + def album_tracks(self, album_id, limit=50, offset=0, market=None): + """ Get Spotify catalog information about an album's tracks + + Parameters: + - album_id - the album ID, URI or URL + - limit - the number of items to return + - offset - the index of the first item to return + - market - an ISO 3166-1 alpha-2 country code. + + """ + + trid = self._get_id("album", album_id) + return self._get( + "albums/" + trid + "/tracks/", limit=limit, offset=offset, market=market + ) + + def albums(self, albums, market=None): + """ returns a list of albums given the album IDs, URIs, or URLs + + Parameters: + - albums - a list of album IDs, URIs or URLs + - market - an ISO 3166-1 alpha-2 country code + """ + + tlist = [self._get_id("album", a) for a in albums] + if market is not None: + return self._get("albums/?ids=" + ",".join(tlist) + '&market=' + market) + else: + return self._get("albums/?ids=" + ",".join(tlist)) + + def show(self, show_id, market=None): + """ returns a single show given the show's ID, URIs or URL + + Parameters: + - show_id - the show ID, URI or URL + - market - an ISO 3166-1 alpha-2 country code. + The show must be available in the given market. + If user-based authorization is in use, the user's country + takes precedence. If neither market nor user country are + provided, the content is considered unavailable for the client. + """ + + trid = self._get_id("show", show_id) + return self._get("shows/" + trid, market=market) + + def shows(self, shows, market=None): + """ returns a list of shows given the show IDs, URIs, or URLs + + Parameters: + - shows - a list of show IDs, URIs or URLs + - market - an ISO 3166-1 alpha-2 country code. + Only shows available in the given market will be returned. + If user-based authorization is in use, the user's country + takes precedence. If neither market nor user country are + provided, the content is considered unavailable for the client. + """ + + tlist = [self._get_id("show", s) for s in shows] + return self._get("shows/?ids=" + ",".join(tlist), market=market) + + def show_episodes(self, show_id, limit=50, offset=0, market=None): + """ Get Spotify catalog information about a show's episodes + + Parameters: + - show_id - the show ID, URI or URL + - limit - the number of items to return + - offset - the index of the first item to return + - market - an ISO 3166-1 alpha-2 country code. + Only episodes available in the given market will be returned. + If user-based authorization is in use, the user's country + takes precedence. If neither market nor user country are + provided, the content is considered unavailable for the client. + """ + + trid = self._get_id("show", show_id) + return self._get( + "shows/" + trid + "/episodes/", limit=limit, offset=offset, market=market + ) + + def episode(self, episode_id, market=None): + """ returns a single episode given the episode's ID, URIs or URL + + Parameters: + - episode_id - the episode ID, URI or URL + - market - an ISO 3166-1 alpha-2 country code. + The episode must be available in the given market. + If user-based authorization is in use, the user's country + takes precedence. If neither market nor user country are + provided, the content is considered unavailable for the client. + """ + + trid = self._get_id("episode", episode_id) + return self._get("episodes/" + trid, market=market) + + def episodes(self, episodes, market=None): + """ returns a list of episodes given the episode IDs, URIs, or URLs + + Parameters: + - episodes - a list of episode IDs, URIs or URLs + - market - an ISO 3166-1 alpha-2 country code. + Only episodes available in the given market will be returned. + If user-based authorization is in use, the user's country + takes precedence. If neither market nor user country are + provided, the content is considered unavailable for the client. + """ + + tlist = [self._get_id("episode", e) for e in episodes] + return self._get("episodes/?ids=" + ",".join(tlist), market=market) + + def search(self, q, limit=10, offset=0, type="track", market=None): + """ searches for an item + + Parameters: + - q - the search query (see how to write a query in the + official documentation https://developer.spotify.com/documentation/web-api/reference/search/) # noqa + - limit - the number of items to return (min = 1, default = 10, max = 50). The limit is applied + within each type, not on the total response. + - offset - the index of the first item to return + - type - the types of items to return. One or more of 'artist', 'album', + 'track', 'playlist', 'show', and 'episode'. If multiple types are desired, + pass in a comma separated string; e.g., 'track,album,episode'. + - market - An ISO 3166-1 alpha-2 country code or the string + from_token. + """ + return self._get( + "search", q=q, limit=limit, offset=offset, type=type, market=market + ) + + def search_markets(self, q, limit=10, offset=0, type="track", markets=None, total=None): + """ (experimental) Searches multiple markets for an item + + Parameters: + - q - the search query (see how to write a query in the + official documentation https://developer.spotify.com/documentation/web-api/reference/search/) # noqa + - limit - the number of items to return (min = 1, default = 10, max = 50). If a search is to be done on multiple + markets, then this limit is applied to each market. (e.g. search US, CA, MX each with a limit of 10). + If multiple types are specified, this applies to each type. + - offset - the index of the first item to return + - type - the types of items to return. One or more of 'artist', 'album', + 'track', 'playlist', 'show', or 'episode'. If multiple types are desired, pass in a comma separated string. + - markets - A list of ISO 3166-1 alpha-2 country codes. Search all country markets by default. + - total - the total number of results to return across multiple markets and types. + """ + warnings.warn( + "Searching multiple markets is an experimental feature. " + "Please be aware that this method's inputs and outputs can change in the future.", + UserWarning, + ) + if not markets: + markets = self.country_codes + + if not (isinstance(markets, list) or isinstance(markets, tuple)): + markets = [] + + warnings.warn( + "Searching multiple markets is poorly performing.", + UserWarning, + ) + return self._search_multiple_markets(q, limit, offset, type, markets, total) + + def user(self, user): + """ Gets basic profile information about a Spotify User + + Parameters: + - user - the id of the usr + """ + return self._get("users/" + user) + + def current_user_playlists(self, limit=50, offset=0): + """ Get current user playlists without required getting his profile + Parameters: + - limit - the number of items to return + - offset - the index of the first item to return + """ + return self._get("me/playlists", limit=limit, offset=offset) + + def playlist(self, playlist_id, fields=None, market=None, additional_types=("track",)): + """ Gets playlist by id. + + Parameters: + - playlist - the id of the playlist + - fields - which fields to return + - market - An ISO 3166-1 alpha-2 country code or the + string from_token. + - additional_types - list of item types to return. + valid types are: track and episode + """ + plid = self._get_id("playlist", playlist_id) + return self._get( + "playlists/%s" % (plid), + fields=fields, + market=market, + additional_types=",".join(additional_types), + ) + + def playlist_tracks( + self, + playlist_id, + fields=None, + limit=100, + offset=0, + market=None, + additional_types=("track",) + ): + """ Get full details of the tracks of a playlist. + + Parameters: + - playlist_id - the playlist ID, URI or URL + - fields - which fields to return + - limit - the maximum number of tracks to return + - offset - the index of the first track to return + - market - an ISO 3166-1 alpha-2 country code. + - additional_types - list of item types to return. + valid types are: track and episode + """ + warnings.warn( + "You should use `playlist_items(playlist_id, ...," + "additional_types=('track',))` instead", + DeprecationWarning, + ) + return self.playlist_items(playlist_id, fields, limit, offset, + market, additional_types) + + def playlist_items( + self, + playlist_id, + fields=None, + limit=100, + offset=0, + market=None, + additional_types=("track", "episode") + ): + """ Get full details of the tracks and episodes of a playlist. + + Parameters: + - playlist_id - the playlist ID, URI or URL + - fields - which fields to return + - limit - the maximum number of tracks to return + - offset - the index of the first track to return + - market - an ISO 3166-1 alpha-2 country code. + - additional_types - list of item types to return. + valid types are: track and episode + """ + plid = self._get_id("playlist", playlist_id) + return self._get( + "playlists/%s/tracks" % (plid), + limit=limit, + offset=offset, + fields=fields, + market=market, + additional_types=",".join(additional_types) + ) + + def playlist_cover_image(self, playlist_id): + """ Get cover image of a playlist. + + Parameters: + - playlist_id - the playlist ID, URI or URL + """ + plid = self._get_id("playlist", playlist_id) + return self._get("playlists/%s/images" % (plid)) + + def playlist_upload_cover_image(self, playlist_id, image_b64): + """ Replace the image used to represent a specific playlist + + Parameters: + - playlist_id - the id of the playlist + - image_b64 - image data as a Base64 encoded JPEG image string + (maximum payload size is 256 KB) + """ + plid = self._get_id("playlist", playlist_id) + return self._put( + "playlists/{}/images".format(plid), + payload=image_b64, + content_type="image/jpeg", + ) + + def user_playlist(self, user, playlist_id=None, fields=None, market=None): + warnings.warn( + "You should use `playlist(playlist_id)` instead", + DeprecationWarning, + ) + + """ Gets a single playlist of a user + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - fields - which fields to return + """ + if playlist_id is None: + return self._get("users/%s/starred" % user) + return self.playlist(playlist_id, fields=fields, market=market) + + def user_playlist_tracks( + self, + user=None, + playlist_id=None, + fields=None, + limit=100, + offset=0, + market=None, + ): + warnings.warn( + "You should use `playlist_tracks(playlist_id)` instead", + DeprecationWarning, + ) + + """ Get full details of the tracks of a playlist owned by a user. + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - fields - which fields to return + - limit - the maximum number of tracks to return + - offset - the index of the first track to return + - market - an ISO 3166-1 alpha-2 country code. + """ + return self.playlist_tracks( + playlist_id, + limit=limit, + offset=offset, + fields=fields, + market=market, + ) + + def user_playlists(self, user, limit=50, offset=0): + """ Gets playlists of a user + + Parameters: + - user - the id of the usr + - limit - the number of items to return + - offset - the index of the first item to return + """ + return self._get( + "users/%s/playlists" % user, limit=limit, offset=offset + ) + + def user_playlist_create(self, user, name, public=True, collaborative=False, description=""): + """ Creates a playlist for a user + + Parameters: + - user - the id of the user + - name - the name of the playlist + - public - is the created playlist public + - collaborative - is the created playlist collaborative + - description - the description of the playlist + """ + data = { + "name": name, + "public": public, + "collaborative": collaborative, + "description": description + } + + return self._post("users/%s/playlists" % (user,), payload=data) + + def user_playlist_change_details( + self, + user, + playlist_id, + name=None, + public=None, + collaborative=None, + description=None, + ): + warnings.warn( + "You should use `playlist_change_details(playlist_id, ...)` instead", + DeprecationWarning, + ) + """ Changes a playlist's name and/or public/private state + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - name - optional name of the playlist + - public - optional is the playlist public + - collaborative - optional is the playlist collaborative + - description - optional description of the playlist + """ + + return self.playlist_change_details(playlist_id, name, public, + collaborative, description) + + def user_playlist_unfollow(self, user, playlist_id): + """ Unfollows (deletes) a playlist for a user + + Parameters: + - user - the id of the user + - name - the name of the playlist + """ + warnings.warn( + "You should use `current_user_unfollow_playlist(playlist_id)` instead", + DeprecationWarning, + ) + return self.current_user_unfollow_playlist(playlist_id) + + def user_playlist_add_tracks( + self, user, playlist_id, tracks, position=None + ): + warnings.warn( + "You should use `playlist_add_items(playlist_id, tracks)` instead", + DeprecationWarning, + ) + """ Adds tracks to a playlist + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - tracks - a list of track URIs, URLs or IDs + - position - the position to add the tracks + """ + tracks = [self._get_uri("track", tid) for tid in tracks] + return self.playlist_add_items(playlist_id, tracks, position) + + def user_playlist_add_episodes( + self, user, playlist_id, episodes, position=None + ): + warnings.warn( + "You should use `playlist_add_items(playlist_id, episodes)` instead", + DeprecationWarning, + ) + """ Adds episodes to a playlist + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - episodes - a list of track URIs, URLs or IDs + - position - the position to add the episodes + """ + episodes = [self._get_uri("episode", tid) for tid in episodes] + return self.playlist_add_items(playlist_id, episodes, position) + + def user_playlist_replace_tracks(self, user, playlist_id, tracks): + """ Replace all tracks in a playlist for a user + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - tracks - the list of track ids to add to the playlist + """ + warnings.warn( + "You should use `playlist_replace_items(playlist_id, tracks)` instead", + DeprecationWarning, + ) + return self.playlist_replace_items(playlist_id, tracks) + + def user_playlist_reorder_tracks( + self, + user, + playlist_id, + range_start, + insert_before, + range_length=1, + snapshot_id=None, + ): + """ Reorder tracks in a playlist from a user + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - range_start - the position of the first track to be reordered + - range_length - optional the number of tracks to be reordered + (default: 1) + - insert_before - the position where the tracks should be + inserted + - snapshot_id - optional playlist's snapshot ID + """ + warnings.warn( + "You should use `playlist_reorder_items(playlist_id, ...)` instead", + DeprecationWarning, + ) + return self.playlist_reorder_items(playlist_id, range_start, + insert_before, range_length, + snapshot_id) + + def user_playlist_remove_all_occurrences_of_tracks( + self, user, playlist_id, tracks, snapshot_id=None + ): + """ Removes all occurrences of the given tracks from the given playlist + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - tracks - the list of track ids to remove from the playlist + - snapshot_id - optional id of the playlist snapshot + + """ + warnings.warn( + "You should use `playlist_remove_all_occurrences_of_items" + "(playlist_id, tracks)` instead", + DeprecationWarning, + ) + return self.playlist_remove_all_occurrences_of_items(playlist_id, + tracks, + snapshot_id) + + def user_playlist_remove_specific_occurrences_of_tracks( + self, user, playlist_id, tracks, snapshot_id=None + ): + """ Removes all occurrences of the given tracks from the given playlist + + Parameters: + - user - the id of the user + - playlist_id - the id of the playlist + - tracks - an array of objects containing Spotify URIs of the + tracks to remove with their current positions in the + playlist. For example: + [ { "uri":"4iV5W9uYEdYUVa79Axb7Rh", "positions":[2] }, + { "uri":"1301WleyT98MSxVHPZCA6M", "positions":[7] } ] + - snapshot_id - optional id of the playlist snapshot + """ + warnings.warn( + "You should use `playlist_remove_specific_occurrences_of_items" + "(playlist_id, tracks)` instead", + DeprecationWarning, + ) + plid = self._get_id("playlist", playlist_id) + ftracks = [] + for tr in tracks: + ftracks.append( + { + "uri": self._get_uri("track", tr["uri"]), + "positions": tr["positions"], + } + ) + payload = {"tracks": ftracks} + if snapshot_id: + payload["snapshot_id"] = snapshot_id + return self._delete( + "users/%s/playlists/%s/tracks" % (user, plid), payload=payload + ) + + def user_playlist_follow_playlist(self, playlist_owner_id, playlist_id): + """ + Add the current authenticated user as a follower of a playlist. + + Parameters: + - playlist_owner_id - the user id of the playlist owner + - playlist_id - the id of the playlist + + """ + warnings.warn( + "You should use `current_user_follow_playlist(playlist_id)` instead", + DeprecationWarning, + ) + return self.current_user_follow_playlist(playlist_id) + + def user_playlist_is_following( + self, playlist_owner_id, playlist_id, user_ids + ): + """ + Check to see if the given users are following the given playlist + + Parameters: + - playlist_owner_id - the user id of the playlist owner + - playlist_id - the id of the playlist + - user_ids - the ids of the users that you want to check to see + if they follow the playlist. Maximum: 5 ids. + + """ + warnings.warn( + "You should use `playlist_is_following(playlist_id, user_ids)` instead", + DeprecationWarning, + ) + return self.playlist_is_following(playlist_id, user_ids) + + def playlist_change_details( + self, + playlist_id, + name=None, + public=None, + collaborative=None, + description=None, + ): + """ Changes a playlist's name and/or public/private state, + collaborative state, and/or description + + Parameters: + - playlist_id - the id of the playlist + - name - optional name of the playlist + - public - optional is the playlist public + - collaborative - optional is the playlist collaborative + - description - optional description of the playlist + """ + + data = {} + if isinstance(name, six.string_types): + data["name"] = name + if isinstance(public, bool): + data["public"] = public + if isinstance(collaborative, bool): + data["collaborative"] = collaborative + if isinstance(description, six.string_types): + data["description"] = description + return self._put( + "playlists/%s" % (self._get_id("playlist", playlist_id)), payload=data + ) + + def current_user_unfollow_playlist(self, playlist_id): + """ Unfollows (deletes) a playlist for the current authenticated + user + + Parameters: + - name - the name of the playlist + """ + return self._delete( + "playlists/%s/followers" % (playlist_id) + ) + + def playlist_add_items( + self, playlist_id, items, position=None + ): + """ Adds tracks/episodes to a playlist + + Parameters: + - playlist_id - the id of the playlist + - items - a list of track/episode URIs or URLs + - position - the position to add the tracks + """ + plid = self._get_id("playlist", playlist_id) + ftracks = [self._get_uri("track", tid) for tid in items] + return self._post( + "playlists/%s/tracks" % (plid), + payload=ftracks, + position=position, + ) + + def playlist_replace_items(self, playlist_id, items): + """ Replace all tracks/episodes in a playlist + + Parameters: + - playlist_id - the id of the playlist + - items - list of track/episode ids to comprise playlist + """ + plid = self._get_id("playlist", playlist_id) + ftracks = [self._get_uri("track", tid) for tid in items] + payload = {"uris": ftracks} + return self._put( + "playlists/%s/tracks" % (plid), payload=payload + ) + + def playlist_reorder_items( + self, + playlist_id, + range_start, + insert_before, + range_length=1, + snapshot_id=None, + ): + """ Reorder tracks in a playlist + + Parameters: + - playlist_id - the id of the playlist + - range_start - the position of the first track to be reordered + - range_length - optional the number of tracks to be reordered + (default: 1) + - insert_before - the position where the tracks should be + inserted + - snapshot_id - optional playlist's snapshot ID + """ + plid = self._get_id("playlist", playlist_id) + payload = { + "range_start": range_start, + "range_length": range_length, + "insert_before": insert_before, + } + if snapshot_id: + payload["snapshot_id"] = snapshot_id + return self._put( + "playlists/%s/tracks" % (plid), payload=payload + ) + + def playlist_remove_all_occurrences_of_items( + self, playlist_id, items, snapshot_id=None + ): + """ Removes all occurrences of the given tracks/episodes from the given playlist + + Parameters: + - playlist_id - the id of the playlist + - items - list of track/episode ids to remove from the playlist + - snapshot_id - optional id of the playlist snapshot + + """ + + plid = self._get_id("playlist", playlist_id) + ftracks = [self._get_uri("track", tid) for tid in items] + payload = {"tracks": [{"uri": track} for track in ftracks]} + if snapshot_id: + payload["snapshot_id"] = snapshot_id + return self._delete( + "playlists/%s/tracks" % (plid), payload=payload + ) + + def playlist_remove_specific_occurrences_of_items( + self, playlist_id, items, snapshot_id=None + ): + """ Removes all occurrences of the given tracks from the given playlist + + Parameters: + - playlist_id - the id of the playlist + - items - an array of objects containing Spotify URIs of the + tracks/episodes to remove with their current positions in + the playlist. For example: + [ { "uri":"4iV5W9uYEdYUVa79Axb7Rh", "positions":[2] }, + { "uri":"1301WleyT98MSxVHPZCA6M", "positions":[7] } ] + - snapshot_id - optional id of the playlist snapshot + """ + + plid = self._get_id("playlist", playlist_id) + ftracks = [] + for tr in items: + ftracks.append( + { + "uri": self._get_uri("track", tr["uri"]), + "positions": tr["positions"], + } + ) + payload = {"tracks": ftracks} + if snapshot_id: + payload["snapshot_id"] = snapshot_id + return self._delete( + "playlists/%s/tracks" % (plid), payload=payload + ) + + def current_user_follow_playlist(self, playlist_id): + """ + Add the current authenticated user as a follower of a playlist. + + Parameters: + - playlist_id - the id of the playlist + + """ + return self._put( + "playlists/{}/followers".format(playlist_id) + ) + + def playlist_is_following( + self, playlist_id, user_ids + ): + """ + Check to see if the given users are following the given playlist + + Parameters: + - playlist_id - the id of the playlist + - user_ids - the ids of the users that you want to check to see + if they follow the playlist. Maximum: 5 ids. + + """ + endpoint = "playlists/{}/followers/contains?ids={}" + return self._get( + endpoint.format(playlist_id, ",".join(user_ids)) + ) + + def me(self): + """ Get detailed profile information about the current user. + An alias for the 'current_user' method. + """ + return self._get("me/") + + def current_user(self): + """ Get detailed profile information about the current user. + An alias for the 'me' method. + """ + return self.me() + + def current_user_playing_track(self): + """ Get information about the current users currently playing track. + """ + return self._get("me/player/currently-playing") + + def current_user_saved_albums(self, limit=20, offset=0, market=None): + """ Gets a list of the albums saved in the current authorized user's + "Your Music" library + + Parameters: + - limit - the number of albums to return (MAX_LIMIT=50) + - offset - the index of the first album to return + - market - an ISO 3166-1 alpha-2 country code. + + """ + return self._get("me/albums", limit=limit, offset=offset, market=market) + + def current_user_saved_albums_add(self, albums=[]): + """ Add one or more albums to the current user's + "Your Music" library. + Parameters: + - albums - a list of album URIs, URLs or IDs + """ + + alist = [self._get_id("album", a) for a in albums] + return self._put("me/albums?ids=" + ",".join(alist)) + + def current_user_saved_albums_delete(self, albums=[]): + """ Remove one or more albums from the current user's + "Your Music" library. + + Parameters: + - albums - a list of album URIs, URLs or IDs + """ + alist = [self._get_id("album", a) for a in albums] + return self._delete("me/albums/?ids=" + ",".join(alist)) + + def current_user_saved_albums_contains(self, albums=[]): + """ Check if one or more albums is already saved in + the current Spotify user’s “Your Music” library. + + Parameters: + - albums - a list of album URIs, URLs or IDs + """ + alist = [self._get_id("album", a) for a in albums] + return self._get("me/albums/contains?ids=" + ",".join(alist)) + + def current_user_saved_tracks(self, limit=20, offset=0, market=None): + """ Gets a list of the tracks saved in the current authorized user's + "Your Music" library + + Parameters: + - limit - the number of tracks to return + - offset - the index of the first track to return + - market - an ISO 3166-1 alpha-2 country code + + """ + return self._get("me/tracks", limit=limit, offset=offset, market=market) + + def current_user_saved_tracks_add(self, tracks=None): + """ Add one or more tracks to the current user's + "Your Music" library. + + Parameters: + - tracks - a list of track URIs, URLs or IDs + """ + tlist = [] + if tracks is not None: + tlist = [self._get_id("track", t) for t in tracks] + return self._put("me/tracks/?ids=" + ",".join(tlist)) + + def current_user_saved_tracks_delete(self, tracks=None): + """ Remove one or more tracks from the current user's + "Your Music" library. + + Parameters: + - tracks - a list of track URIs, URLs or IDs + """ + tlist = [] + if tracks is not None: + tlist = [self._get_id("track", t) for t in tracks] + return self._delete("me/tracks/?ids=" + ",".join(tlist)) + + def current_user_saved_tracks_contains(self, tracks=None): + """ Check if one or more tracks is already saved in + the current Spotify user’s “Your Music” library. + + Parameters: + - tracks - a list of track URIs, URLs or IDs + """ + tlist = [] + if tracks is not None: + tlist = [self._get_id("track", t) for t in tracks] + return self._get("me/tracks/contains?ids=" + ",".join(tlist)) + + def current_user_saved_episodes(self, limit=20, offset=0, market=None): + """ Gets a list of the episodes saved in the current authorized user's + "Your Music" library + + Parameters: + - limit - the number of episodes to return + - offset - the index of the first episode to return + - market - an ISO 3166-1 alpha-2 country code + + """ + return self._get("me/episodes", limit=limit, offset=offset, market=market) + + def current_user_saved_episodes_add(self, episodes=None): + """ Add one or more episodes to the current user's + "Your Music" library. + + Parameters: + - episodes - a list of episode URIs, URLs or IDs + """ + elist = [] + if episodes is not None: + elist = [self._get_id("episode", e) for e in episodes] + return self._put("me/episodes/?ids=" + ",".join(elist)) + + def current_user_saved_episodes_delete(self, episodes=None): + """ Remove one or more episodes from the current user's + "Your Music" library. + + Parameters: + - episodes - a list of episode URIs, URLs or IDs + """ + elist = [] + if episodes is not None: + elist = [self._get_id("episode", e) for e in episodes] + return self._delete("me/episodes/?ids=" + ",".join(elist)) + + def current_user_saved_episodes_contains(self, episodes=None): + """ Check if one or more episodes is already saved in + the current Spotify user’s “Your Music” library. + + Parameters: + - episodes - a list of episode URIs, URLs or IDs + """ + elist = [] + if episodes is not None: + elist = [self._get_id("episode", e) for e in episodes] + return self._get("me/episodes/contains?ids=" + ",".join(elist)) + + def current_user_saved_shows(self, limit=20, offset=0, market=None): + """ Gets a list of the shows saved in the current authorized user's + "Your Music" library + + Parameters: + - limit - the number of shows to return + - offset - the index of the first show to return + - market - an ISO 3166-1 alpha-2 country code + + """ + return self._get("me/shows", limit=limit, offset=offset, market=market) + + def current_user_saved_shows_add(self, shows=[]): + """ Add one or more albums to the current user's + "Your Music" library. + Parameters: + - shows - a list of show URIs, URLs or IDs + """ + slist = [self._get_id("show", s) for s in shows] + return self._put("me/shows?ids=" + ",".join(slist)) + + def current_user_saved_shows_delete(self, shows=[]): + """ Remove one or more shows from the current user's + "Your Music" library. + + Parameters: + - shows - a list of show URIs, URLs or IDs + """ + slist = [self._get_id("show", s) for s in shows] + return self._delete("me/shows/?ids=" + ",".join(slist)) + + def current_user_saved_shows_contains(self, shows=[]): + """ Check if one or more shows is already saved in + the current Spotify user’s “Your Music” library. + + Parameters: + - shows - a list of show URIs, URLs or IDs + """ + slist = [self._get_id("show", s) for s in shows] + return self._get("me/shows/contains?ids=" + ",".join(slist)) + + def current_user_followed_artists(self, limit=20, after=None): + """ Gets a list of the artists followed by the current authorized user + + Parameters: + - limit - the number of artists to return + - after - the last artist ID retrieved from the previous + request + + """ + return self._get( + "me/following", type="artist", limit=limit, after=after + ) + + def current_user_following_artists(self, ids=None): + """ Check if the current user is following certain artists + + Returns list of booleans respective to ids + + Parameters: + - ids - a list of artist URIs, URLs or IDs + """ + idlist = [] + if ids is not None: + idlist = [self._get_id("artist", i) for i in ids] + return self._get( + "me/following/contains", ids=",".join(idlist), type="artist" + ) + + def current_user_following_users(self, ids=None): + """ Check if the current user is following certain users + + Returns list of booleans respective to ids + + Parameters: + - ids - a list of user URIs, URLs or IDs + """ + idlist = [] + if ids is not None: + idlist = [self._get_id("user", i) for i in ids] + return self._get( + "me/following/contains", ids=",".join(idlist), type="user" + ) + + def current_user_top_artists( + self, limit=20, offset=0, time_range="medium_term" + ): + """ Get the current user's top artists + + Parameters: + - limit - the number of entities to return + - offset - the index of the first entity to return + - time_range - Over what time frame are the affinities computed + Valid-values: short_term, medium_term, long_term + """ + return self._get( + "me/top/artists", time_range=time_range, limit=limit, offset=offset + ) + + def current_user_top_tracks( + self, limit=20, offset=0, time_range="medium_term" + ): + """ Get the current user's top tracks + + Parameters: + - limit - the number of entities to return + - offset - the index of the first entity to return + - time_range - Over what time frame are the affinities computed + Valid-values: short_term, medium_term, long_term + """ + return self._get( + "me/top/tracks", time_range=time_range, limit=limit, offset=offset + ) + + def current_user_recently_played(self, limit=50, after=None, before=None): + """ Get the current user's recently played tracks + + Parameters: + - limit - the number of entities to return + - after - unix timestamp in milliseconds. Returns all items + after (but not including) this cursor position. + Cannot be used if before is specified. + - before - unix timestamp in milliseconds. Returns all items + before (but not including) this cursor position. + Cannot be used if after is specified + """ + return self._get( + "me/player/recently-played", + limit=limit, + after=after, + before=before, + ) + + def user_follow_artists(self, ids=[]): + """ Follow one or more artists + Parameters: + - ids - a list of artist IDs + """ + return self._put("me/following?type=artist&ids=" + ",".join(ids)) + + def user_follow_users(self, ids=[]): + """ Follow one or more users + Parameters: + - ids - a list of user IDs + """ + return self._put("me/following?type=user&ids=" + ",".join(ids)) + + def user_unfollow_artists(self, ids=[]): + """ Unfollow one or more artists + Parameters: + - ids - a list of artist IDs + """ + return self._delete("me/following?type=artist&ids=" + ",".join(ids)) + + def user_unfollow_users(self, ids=[]): + """ Unfollow one or more users + Parameters: + - ids - a list of user IDs + """ + return self._delete("me/following?type=user&ids=" + ",".join(ids)) + + def featured_playlists( + self, locale=None, country=None, timestamp=None, limit=20, offset=0 + ): + """ Get a list of Spotify featured playlists + + Parameters: + - locale - The desired language, consisting of a lowercase ISO + 639-1 alpha-2 language code and an uppercase ISO 3166-1 alpha-2 + country code, joined by an underscore. + + - country - An ISO 3166-1 alpha-2 country code. + + - timestamp - A timestamp in ISO 8601 format: + yyyy-MM-ddTHH:mm:ss. Use this parameter to specify the user's + local time to get results tailored for that specific date and + time in the day + + - limit - The maximum number of items to return. Default: 20. + Minimum: 1. Maximum: 50 + + - offset - The index of the first item to return. Default: 0 + (the first object). Use with limit to get the next set of + items. + """ + return self._get( + "browse/featured-playlists", + locale=locale, + country=country, + timestamp=timestamp, + limit=limit, + offset=offset, + ) + + def new_releases(self, country=None, limit=20, offset=0): + """ Get a list of new album releases featured in Spotify + + Parameters: + - country - An ISO 3166-1 alpha-2 country code. + + - limit - The maximum number of items to return. Default: 20. + Minimum: 1. Maximum: 50 + + - offset - The index of the first item to return. Default: 0 + (the first object). Use with limit to get the next set of + items. + """ + return self._get( + "browse/new-releases", country=country, limit=limit, offset=offset + ) + + def category(self, category_id, country=None, locale=None): + """ Get info about a category + + Parameters: + - category_id - The Spotify category ID for the category. + + - country - An ISO 3166-1 alpha-2 country code. + - locale - The desired language, consisting of an ISO 639-1 alpha-2 + language code and an ISO 3166-1 alpha-2 country code, joined + by an underscore. + """ + return self._get( + "browse/categories/" + category_id, + country=country, + locale=locale, + ) + + def categories(self, country=None, locale=None, limit=20, offset=0): + """ Get a list of categories + + Parameters: + - country - An ISO 3166-1 alpha-2 country code. + - locale - The desired language, consisting of an ISO 639-1 alpha-2 + language code and an ISO 3166-1 alpha-2 country code, joined + by an underscore. + + - limit - The maximum number of items to return. Default: 20. + Minimum: 1. Maximum: 50 + + - offset - The index of the first item to return. Default: 0 + (the first object). Use with limit to get the next set of + items. + """ + return self._get( + "browse/categories", + country=country, + locale=locale, + limit=limit, + offset=offset, + ) + + def category_playlists( + self, category_id=None, country=None, limit=20, offset=0 + ): + """ Get a list of playlists for a specific Spotify category + + Parameters: + - category_id - The Spotify category ID for the category. + + - country - An ISO 3166-1 alpha-2 country code. + + - limit - The maximum number of items to return. Default: 20. + Minimum: 1. Maximum: 50 + + - offset - The index of the first item to return. Default: 0 + (the first object). Use with limit to get the next set of + items. + """ + return self._get( + "browse/categories/" + category_id + "/playlists", + country=country, + limit=limit, + offset=offset, + ) + + def recommendations( + self, + seed_artists=None, + seed_genres=None, + seed_tracks=None, + limit=20, + country=None, + **kwargs + ): + """ Get a list of recommended tracks for one to five seeds. + (at least one of `seed_artists`, `seed_tracks` and `seed_genres` + are needed) + + Parameters: + - seed_artists - a list of artist IDs, URIs or URLs + - seed_tracks - a list of track IDs, URIs or URLs + - seed_genres - a list of genre names. Available genres for + recommendations can be found by calling + recommendation_genre_seeds + + - country - An ISO 3166-1 alpha-2 country code. If provided, + all results will be playable in this country. + + - limit - The maximum number of items to return. Default: 20. + Minimum: 1. Maximum: 100 + + - min/max/target_ - For the tuneable track + attributes listed in the documentation, these values + provide filters and targeting on results. + """ + params = dict(limit=limit) + if seed_artists: + params["seed_artists"] = ",".join( + [self._get_id("artist", a) for a in seed_artists] + ) + if seed_genres: + params["seed_genres"] = ",".join(seed_genres) + if seed_tracks: + params["seed_tracks"] = ",".join( + [self._get_id("track", t) for t in seed_tracks] + ) + if country: + params["market"] = country + + for attribute in [ + "acousticness", + "danceability", + "duration_ms", + "energy", + "instrumentalness", + "key", + "liveness", + "loudness", + "mode", + "popularity", + "speechiness", + "tempo", + "time_signature", + "valence", + ]: + for prefix in ["min_", "max_", "target_"]: + param = prefix + attribute + if param in kwargs: + params[param] = kwargs[param] + return self._get("recommendations", **params) + + def recommendation_genre_seeds(self): + """ Get a list of genres available for the recommendations function. + """ + return self._get("recommendations/available-genre-seeds") + + def audio_analysis(self, track_id): + """ Get audio analysis for a track based upon its Spotify ID + Parameters: + - track_id - a track URI, URL or ID + """ + trid = self._get_id("track", track_id) + return self._get("audio-analysis/" + trid) + + def audio_features(self, tracks=[]): + """ Get audio features for one or multiple tracks based upon their Spotify IDs + Parameters: + - tracks - a list of track URIs, URLs or IDs, maximum: 100 ids + """ + if isinstance(tracks, str): + trackid = self._get_id("track", tracks) + results = self._get("audio-features/?ids=" + trackid) + else: + tlist = [self._get_id("track", t) for t in tracks] + results = self._get("audio-features/?ids=" + ",".join(tlist)) + # the response has changed, look for the new style first, and if + # its not there, fallback on the old style + if "audio_features" in results: + return results["audio_features"] + else: + return results + + def devices(self): + """ Get a list of user's available devices. + """ + return self._get("me/player/devices") + + def current_playback(self, market=None, additional_types=None): + """ Get information about user's current playback. + + Parameters: + - market - an ISO 3166-1 alpha-2 country code. + - additional_types - `episode` to get podcast track information + """ + return self._get("me/player", market=market, additional_types=additional_types) + + def currently_playing(self, market=None, additional_types=None): + """ Get user's currently playing track. + + Parameters: + - market - an ISO 3166-1 alpha-2 country code. + - additional_types - `episode` to get podcast track information + """ + return self._get("me/player/currently-playing", market=market, + additional_types=additional_types) + + def transfer_playback(self, device_id, force_play=True): + """ Transfer playback to another device. + Note that the API accepts a list of device ids, but only + actually supports one. + + Parameters: + - device_id - transfer playback to this device + - force_play - true: after transfer, play. false: + keep current state. + """ + data = {"device_ids": [device_id], "play": force_play} + return self._put("me/player", payload=data) + + def start_playback( + self, device_id=None, context_uri=None, uris=None, offset=None, position_ms=None + ): + """ Start or resume user's playback. + + Provide a `context_uri` to start playback of an album, + artist, or playlist. + + Provide a `uris` list to start playback of one or more + tracks. + + Provide `offset` as {"position": } or {"uri": ""} + to start playback at a particular offset. + + Parameters: + - device_id - device target for playback + - context_uri - spotify context uri to play + - uris - spotify track uris + - offset - offset into context by index or track + - position_ms - (optional) indicates from what position to start playback. + Must be a positive number. Passing in a position that is + greater than the length of the track will cause the player to + start playing the next song. + """ + if context_uri is not None and uris is not None: + logger.warning("Specify either context uri or uris, not both") + return + if uris is not None and not isinstance(uris, list): + logger.warning("URIs must be a list") + return + data = {} + if context_uri is not None: + data["context_uri"] = context_uri + if uris is not None: + data["uris"] = uris + if offset is not None: + data["offset"] = offset + if position_ms is not None: + data["position_ms"] = position_ms + return self._put( + self._append_device_id("me/player/play", device_id), payload=data + ) + + def pause_playback(self, device_id=None): + """ Pause user's playback. + + Parameters: + - device_id - device target for playback + """ + return self._put(self._append_device_id("me/player/pause", device_id)) + + def next_track(self, device_id=None): + """ Skip user's playback to next track. + + Parameters: + - device_id - device target for playback + """ + return self._post(self._append_device_id("me/player/next", device_id)) + + def previous_track(self, device_id=None): + """ Skip user's playback to previous track. + + Parameters: + - device_id - device target for playback + """ + return self._post( + self._append_device_id("me/player/previous", device_id) + ) + + def seek_track(self, position_ms, device_id=None): + """ Seek to position in current track. + + Parameters: + - position_ms - position in milliseconds to seek to + - device_id - device target for playback + """ + if not isinstance(position_ms, int): + logger.warning("Position_ms must be an integer") + return + return self._put( + self._append_device_id( + "me/player/seek?position_ms=%s" % position_ms, device_id + ) + ) + + def repeat(self, state, device_id=None): + """ Set repeat mode for playback. + + Parameters: + - state - `track`, `context`, or `off` + - device_id - device target for playback + """ + if state not in ["track", "context", "off"]: + logger.warning("Invalid state") + return + self._put( + self._append_device_id( + "me/player/repeat?state=%s" % state, device_id + ) + ) + + def volume(self, volume_percent, device_id=None): + """ Set playback volume. + + Parameters: + - volume_percent - volume between 0 and 100 + - device_id - device target for playback + """ + if not isinstance(volume_percent, int): + logger.warning("Volume must be an integer") + return + if volume_percent < 0 or volume_percent > 100: + logger.warning("Volume must be between 0 and 100, inclusive") + return + self._put( + self._append_device_id( + "me/player/volume?volume_percent=%s" % volume_percent, + device_id, + ) + ) + + def shuffle(self, state, device_id=None): + """ Toggle playback shuffling. + + Parameters: + - state - true or false + - device_id - device target for playback + """ + if not isinstance(state, bool): + logger.warning("state must be a boolean") + return + state = str(state).lower() + self._put( + self._append_device_id( + "me/player/shuffle?state=%s" % state, device_id + ) + ) + + def queue(self): + """ Gets the current user's queue """ + return self._get("me/player/queue") + + def add_to_queue(self, uri, device_id=None): + """ Adds a song to the end of a user's queue + + If device A is currently playing music and you try to add to the queue + and pass in the id for device B, you will get a + 'Player command failed: Restriction violated' error + I therefore recommend leaving device_id as None so that the active device is targeted + + :param uri: song uri, id, or url + :param device_id: + the id of a Spotify device. + If None, then the active device is used. + + """ + + uri = self._get_uri("track", uri) + + endpoint = "me/player/queue?uri=%s" % uri + + if device_id is not None: + endpoint += "&device_id=%s" % device_id + + return self._post(endpoint) + + def available_markets(self): + """ Get the list of markets where Spotify is available. + Returns a list of the countries in which Spotify is available, identified by their + ISO 3166-1 alpha-2 country code with additional country codes for special territories. + """ + return self._get("markets") + + def _append_device_id(self, path, device_id): + """ Append device ID to API path. + + Parameters: + - device_id - device id to append + """ + if device_id: + if "?" in path: + path += "&device_id=%s" % device_id + else: + path += "?device_id=%s" % device_id + return path + + def _get_id(self, type, id): + uri_match = re.search(Spotify._regex_spotify_uri, id) + if uri_match is not None: + uri_match_groups = uri_match.groupdict() + if uri_match_groups['type'] != type: + # TODO change to a ValueError in v3 + raise SpotifyException(400, -1, "Unexpected Spotify URI type.") + return uri_match_groups['id'] + + url_match = re.search(Spotify._regex_spotify_url, id) + if url_match is not None: + url_match_groups = url_match.groupdict() + if url_match_groups['type'] != type: + raise SpotifyException(400, -1, "Unexpected Spotify URL type.") + # TODO change to a ValueError in v3 + return url_match_groups['id'] + + # Raw identifiers might be passed, ensure they are also base-62 + if re.search(Spotify._regex_base62, id) is not None: + return id + + # TODO change to a ValueError in v3 + raise SpotifyException(400, -1, "Unsupported URL / URI.") + + def _get_uri(self, type, id): + if self._is_uri(id): + return id + else: + return "spotify:" + type + ":" + self._get_id(type, id) + + def _is_uri(self, uri): + return re.search(Spotify._regex_spotify_uri, uri) is not None + + def _search_multiple_markets(self, q, limit, offset, type, markets, total): + if total and limit > total: + limit = total + warnings.warn( + "limit was auto-adjusted to equal {} as it must not be higher than total".format( + total), + UserWarning, + ) + + results = defaultdict(dict) + item_types = [item_type + "s" for item_type in type.split(",")] + count = 0 + + for country in markets: + result = self._get( + "search", q=q, limit=limit, offset=offset, type=type, market=country + ) + for item_type in item_types: + results[country][item_type] = result[item_type] + + # Truncate the items list to the current limit + if len(results[country][item_type]['items']) > limit: + results[country][item_type]['items'] = \ + results[country][item_type]['items'][:limit] + + count += len(results[country][item_type]['items']) + if total and limit > total - count: + # when approaching `total` results, adjust `limit` to not request more + # items than needed + limit = total - count + + if total and count >= total: + return results + + return results diff --git a/resources/lib/deps/spotipy/exceptions.py b/resources/lib/deps/spotipy/exceptions.py new file mode 100644 index 0000000..df503f1 --- /dev/null +++ b/resources/lib/deps/spotipy/exceptions.py @@ -0,0 +1,16 @@ +class SpotifyException(Exception): + + def __init__(self, http_status, code, msg, reason=None, headers=None): + self.http_status = http_status + self.code = code + self.msg = msg + self.reason = reason + # `headers` is used to support `Retry-After` in the event of a + # 429 status code. + if headers is None: + headers = {} + self.headers = headers + + def __str__(self): + return 'http status: {0}, code:{1} - {2}, reason: {3}'.format( + self.http_status, self.code, self.msg, self.reason) diff --git a/resources/lib/deps/spotipy/oauth2.py b/resources/lib/deps/spotipy/oauth2.py new file mode 100644 index 0000000..125c87c --- /dev/null +++ b/resources/lib/deps/spotipy/oauth2.py @@ -0,0 +1,1308 @@ +# -*- coding: utf-8 -*- + +__all__ = [ + "SpotifyClientCredentials", + "SpotifyOAuth", + "SpotifyOauthError", + "SpotifyStateError", + "SpotifyImplicitGrant", + "SpotifyPKCE" +] + +import base64 +import logging +import os +import time +import warnings +import webbrowser + +import requests +# Workaround to support both python 2 & 3 +import six +import six.moves.urllib.parse as urllibparse +from six.moves.BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer +from six.moves.urllib_parse import parse_qsl, urlparse + +from spotipy.cache_handler import CacheFileHandler, CacheHandler +from spotipy.util import CLIENT_CREDS_ENV_VARS, get_host_port, normalize_scope + +logger = logging.getLogger(__name__) + + +class SpotifyOauthError(Exception): + """ Error during Auth Code or Implicit Grant flow """ + + def __init__(self, message, error=None, error_description=None, *args, **kwargs): + self.error = error + self.error_description = error_description + self.__dict__.update(kwargs) + super(SpotifyOauthError, self).__init__(message, *args, **kwargs) + + +class SpotifyStateError(SpotifyOauthError): + """ The state sent and state received were different """ + + def __init__(self, local_state=None, remote_state=None, message=None, + error=None, error_description=None, *args, **kwargs): + if not message: + message = ("Expected " + local_state + " but recieved " + + remote_state) + super(SpotifyOauthError, self).__init__(message, error, + error_description, *args, + **kwargs) + + +def _make_authorization_headers(client_id, client_secret): + auth_header = base64.b64encode( + six.text_type(client_id + ":" + client_secret).encode("ascii") + ) + return {"Authorization": "Basic %s" % auth_header.decode("ascii")} + + +def _ensure_value(value, env_key): + env_val = CLIENT_CREDS_ENV_VARS[env_key] + _val = value or os.getenv(env_val) + if _val is None: + msg = "No %s. Pass it or set a %s environment variable." % ( + env_key, + env_val, + ) + raise SpotifyOauthError(msg) + return _val + + +class SpotifyAuthBase(object): + def __init__(self, requests_session): + if isinstance(requests_session, requests.Session): + self._session = requests_session + else: + if requests_session: # Build a new session. + self._session = requests.Session() + else: # Use the Requests API module as a "session". + from requests import api + self._session = api + + def _normalize_scope(self, scope): + return normalize_scope(scope) + + @property + def client_id(self): + return self._client_id + + @client_id.setter + def client_id(self, val): + self._client_id = _ensure_value(val, "client_id") + + @property + def client_secret(self): + return self._client_secret + + @client_secret.setter + def client_secret(self, val): + self._client_secret = _ensure_value(val, "client_secret") + + @property + def redirect_uri(self): + return self._redirect_uri + + @redirect_uri.setter + def redirect_uri(self, val): + self._redirect_uri = _ensure_value(val, "redirect_uri") + + @staticmethod + def _get_user_input(prompt): + try: + return raw_input(prompt) + except NameError: + return input(prompt) + + @staticmethod + def is_token_expired(token_info): + now = int(time.time()) + return token_info["expires_at"] - now < 60 + + @staticmethod + def _is_scope_subset(needle_scope, haystack_scope): + needle_scope = set(needle_scope.split()) if needle_scope else set() + haystack_scope = ( + set(haystack_scope.split()) if haystack_scope else set() + ) + return needle_scope <= haystack_scope + + def _handle_oauth_error(self, http_error): + response = http_error.response + try: + error_payload = response.json() + error = error_payload.get('error') + error_description = error_payload.get('error_description') + except ValueError: + # if the response cannot be decoded into JSON (which raises a ValueError), + # then try to decode it into text + + # if we receive an empty string (which is falsy), then replace it with `None` + error = response.text or None + error_description = None + + raise SpotifyOauthError( + 'error: {0}, error_description: {1}'.format( + error, error_description + ), + error=error, + error_description=error_description + ) + + def __del__(self): + """Make sure the connection (pool) gets closed""" + if isinstance(self._session, requests.Session): + self._session.close() + + +class SpotifyClientCredentials(SpotifyAuthBase): + OAUTH_TOKEN_URL = "https://accounts.spotify.com/api/token" + + def __init__( + self, + client_id=None, + client_secret=None, + proxies=None, + requests_session=True, + requests_timeout=None, + cache_handler=None + ): + """ + Creates a Client Credentials Flow Manager. + + The Client Credentials flow is used in server-to-server authentication. + Only endpoints that do not access user information can be accessed. + This means that endpoints that require authorization scopes cannot be accessed. + The advantage, however, of this authorization flow is that it does not require any + user interaction + + You can either provide a client_id and client_secret to the + constructor or set SPOTIPY_CLIENT_ID and SPOTIPY_CLIENT_SECRET + environment variables + + Parameters: + * client_id: Must be supplied or set as environment variable + * client_secret: Must be supplied or set as environment variable + * proxies: Optional, proxy for the requests library to route through + * requests_session: A Requests session + * requests_timeout: Optional, tell Requests to stop waiting for a response after + a given number of seconds + * cache_handler: An instance of the `CacheHandler` class to handle + getting and saving cached authorization tokens. + Optional, will otherwise use `CacheFileHandler`. + (takes precedence over `cache_path` and `username`) + + """ + + super(SpotifyClientCredentials, self).__init__(requests_session) + + self.client_id = client_id + self.client_secret = client_secret + self.proxies = proxies + self.requests_timeout = requests_timeout + if cache_handler: + assert issubclass(cache_handler.__class__, CacheHandler), \ + "cache_handler must be a subclass of CacheHandler: " + str(type(cache_handler)) \ + + " != " + str(CacheHandler) + self.cache_handler = cache_handler + else: + self.cache_handler = CacheFileHandler() + + def get_access_token(self, as_dict=True, check_cache=True): + """ + If a valid access token is in memory, returns it + Else fetches a new token and returns it + + Parameters: + - as_dict - a boolean indicating if returning the access token + as a token_info dictionary, otherwise it will be returned + as a string. + """ + if as_dict: + warnings.warn( + "You're using 'as_dict = True'." + "get_access_token will return the token string directly in future " + "versions. Please adjust your code accordingly, or use " + "get_cached_token instead.", + DeprecationWarning, + stacklevel=2, + ) + + if check_cache: + token_info = self.cache_handler.get_cached_token() + if token_info and not self.is_token_expired(token_info): + return token_info if as_dict else token_info["access_token"] + + token_info = self._request_access_token() + token_info = self._add_custom_values_to_token_info(token_info) + self.cache_handler.save_token_to_cache(token_info) + return token_info if as_dict else token_info["access_token"] + + def _request_access_token(self): + """Gets client credentials access token """ + payload = {"grant_type": "client_credentials"} + + headers = _make_authorization_headers( + self.client_id, self.client_secret + ) + + logger.debug( + "sending POST request to %s with Headers: %s and Body: %r", + self.OAUTH_TOKEN_URL, headers, payload + ) + + try: + response = self._session.post( + self.OAUTH_TOKEN_URL, + data=payload, + headers=headers, + verify=True, + proxies=self.proxies, + timeout=self.requests_timeout, + ) + response.raise_for_status() + token_info = response.json() + return token_info + except requests.exceptions.HTTPError as http_error: + self._handle_oauth_error(http_error) + + def _add_custom_values_to_token_info(self, token_info): + """ + Store some values that aren't directly provided by a Web API + response. + """ + token_info["expires_at"] = int(time.time()) + token_info["expires_in"] + return token_info + + +class SpotifyOAuth(SpotifyAuthBase): + """ + Implements Authorization Code Flow for Spotify's OAuth implementation. + """ + OAUTH_AUTHORIZE_URL = "https://accounts.spotify.com/authorize" + OAUTH_TOKEN_URL = "https://accounts.spotify.com/api/token" + + def __init__( + self, + client_id=None, + client_secret=None, + redirect_uri=None, + state=None, + scope=None, + cache_path=None, + username=None, + proxies=None, + show_dialog=False, + requests_session=True, + requests_timeout=None, + open_browser=True, + cache_handler=None + ): + """ + Creates a SpotifyOAuth object + + Parameters: + * client_id: Must be supplied or set as environment variable + * client_secret: Must be supplied or set as environment variable + * redirect_uri: Must be supplied or set as environment variable + * state: Optional, no verification is performed + * scope: Optional, either a list of scopes or comma separated string of scopes. + e.g, "playlist-read-private,playlist-read-collaborative" + * cache_path: (deprecated) Optional, will otherwise be generated + (takes precedence over `username`) + * username: (deprecated) Optional or set as environment variable + (will set `cache_path` to `.cache-{username}`) + * proxies: Optional, proxy for the requests library to route through + * show_dialog: Optional, interpreted as boolean + * requests_session: A Requests session + * requests_timeout: Optional, tell Requests to stop waiting for a response after + a given number of seconds + * open_browser: Optional, whether or not the web browser should be opened to + authorize a user + * cache_handler: An instance of the `CacheHandler` class to handle + getting and saving cached authorization tokens. + Optional, will otherwise use `CacheFileHandler`. + (takes precedence over `cache_path` and `username`) + """ + + super(SpotifyOAuth, self).__init__(requests_session) + + self.client_id = client_id + self.client_secret = client_secret + self.redirect_uri = redirect_uri + self.state = state + self.scope = self._normalize_scope(scope) + if username or cache_path: + warnings.warn("Specifying cache_path or username as arguments to SpotifyOAuth " + + "will be deprecated. Instead, please create a CacheFileHandler " + + "instance with the desired cache_path and username and pass it " + + "to SpotifyOAuth as the cache_handler. For example:\n\n" + + "\tfrom spotipy.oauth2 import CacheFileHandler\n" + + "\thandler = CacheFileHandler(cache_path=cache_path, " + + "username=username)\n" + + "\tsp = spotipy.SpotifyOAuth(client_id, client_secret, " + + "redirect_uri," + + " cache_handler=handler)", + DeprecationWarning + ) + if cache_handler: + warnings.warn("A cache_handler has been specified along with a cache_path or " + + "username. The cache_path and username arguments will be ignored.") + if cache_handler: + assert issubclass(cache_handler.__class__, CacheHandler), \ + "cache_handler must be a subclass of CacheHandler: " + str(type(cache_handler)) \ + + " != " + str(CacheHandler) + self.cache_handler = cache_handler + else: + username = (username or os.getenv(CLIENT_CREDS_ENV_VARS["client_username"])) + self.cache_handler = CacheFileHandler( + username=username, + cache_path=cache_path + ) + self.proxies = proxies + self.requests_timeout = requests_timeout + self.show_dialog = show_dialog + self.open_browser = open_browser + + def validate_token(self, token_info): + if token_info is None: + return None + + # if scopes don't match, then bail + if "scope" not in token_info or not self._is_scope_subset( + self.scope, token_info["scope"] + ): + return None + + if self.is_token_expired(token_info): + token_info = self.refresh_access_token( + token_info["refresh_token"] + ) + + return token_info + + def get_authorize_url(self, state=None): + """ Gets the URL to use to authorize this app + """ + payload = { + "client_id": self.client_id, + "response_type": "code", + "redirect_uri": self.redirect_uri, + } + if self.scope: + payload["scope"] = self.scope + if state is None: + state = self.state + if state is not None: + payload["state"] = state + if self.show_dialog: + payload["show_dialog"] = True + + urlparams = urllibparse.urlencode(payload) + + return "%s?%s" % (self.OAUTH_AUTHORIZE_URL, urlparams) + + def parse_response_code(self, url): + """ Parse the response code in the given response url + + Parameters: + - url - the response url + """ + _, code = self.parse_auth_response_url(url) + if code is None: + return url + else: + return code + + @staticmethod + def parse_auth_response_url(url): + query_s = urlparse(url).query + form = dict(parse_qsl(query_s)) + if "error" in form: + raise SpotifyOauthError("Received error from auth server: " + "{}".format(form["error"]), + error=form["error"]) + return tuple(form.get(param) for param in ["state", "code"]) + + def _make_authorization_headers(self): + return _make_authorization_headers(self.client_id, self.client_secret) + + def _open_auth_url(self): + auth_url = self.get_authorize_url() + try: + webbrowser.open(auth_url) + logger.info("Opened %s in your browser", auth_url) + except webbrowser.Error: + logger.error("Please navigate here: %s", auth_url) + + def _get_auth_response_interactive(self, open_browser=False): + if open_browser: + self._open_auth_url() + prompt = "Enter the URL you were redirected to: " + else: + url = self.get_authorize_url() + prompt = ( + "Go to the following URL: {}\n" + "Enter the URL you were redirected to: ".format(url) + ) + response = self._get_user_input(prompt) + state, code = SpotifyOAuth.parse_auth_response_url(response) + if self.state is not None and self.state != state: + raise SpotifyStateError(self.state, state) + return code + + def _get_auth_response_local_server(self, redirect_port): + server = start_local_http_server(redirect_port) + self._open_auth_url() + server.handle_request() + + if server.error is not None: + raise server.error + elif self.state is not None and server.state != self.state: + raise SpotifyStateError(self.state, server.state) + elif server.auth_code is not None: + return server.auth_code + else: + raise SpotifyOauthError("Server listening on localhost has not been accessed") + + def get_auth_response(self, open_browser=None): + logger.info('User authentication requires interaction with your ' + 'web browser. Once you enter your credentials and ' + 'give authorization, you will be redirected to ' + 'a url. Paste that url you were directed to to ' + 'complete the authorization.') + + redirect_info = urlparse(self.redirect_uri) + redirect_host, redirect_port = get_host_port(redirect_info.netloc) + + if open_browser is None: + open_browser = self.open_browser + + if ( + open_browser + and redirect_host in ("127.0.0.1", "localhost") + and redirect_info.scheme == "http" + ): + # Only start a local http server if a port is specified + if redirect_port: + return self._get_auth_response_local_server(redirect_port) + else: + logger.warning('Using `%s` as redirect URI without a port. ' + 'Specify a port (e.g. `%s:8080`) to allow ' + 'automatic retrieval of authentication code ' + 'instead of having to copy and paste ' + 'the URL your browser is redirected to.', + redirect_host, redirect_host) + + return self._get_auth_response_interactive(open_browser=open_browser) + + def get_authorization_code(self, response=None): + if response: + return self.parse_response_code(response) + return self.get_auth_response() + + def get_access_token(self, code=None, as_dict=True, check_cache=True): + """ Gets the access token for the app given the code + + Parameters: + - code - the response code + - as_dict - a boolean indicating if returning the access token + as a token_info dictionary, otherwise it will be returned + as a string. + """ + if as_dict: + warnings.warn( + "You're using 'as_dict = True'." + "get_access_token will return the token string directly in future " + "versions. Please adjust your code accordingly, or use " + "get_cached_token instead.", + DeprecationWarning, + stacklevel=2, + ) + if check_cache: + token_info = self.validate_token(self.cache_handler.get_cached_token()) + if token_info is not None: + if self.is_token_expired(token_info): + token_info = self.refresh_access_token( + token_info["refresh_token"] + ) + return token_info if as_dict else token_info["access_token"] + + payload = { + "redirect_uri": self.redirect_uri, + "code": code or self.get_auth_response(), + "grant_type": "authorization_code", + } + if self.scope: + payload["scope"] = self.scope + if self.state: + payload["state"] = self.state + + headers = self._make_authorization_headers() + + logger.debug( + "sending POST request to %s with Headers: %s and Body: %r", + self.OAUTH_TOKEN_URL, headers, payload + ) + + try: + response = self._session.post( + self.OAUTH_TOKEN_URL, + data=payload, + headers=headers, + verify=True, + proxies=self.proxies, + timeout=self.requests_timeout, + ) + response.raise_for_status() + token_info = response.json() + token_info = self._add_custom_values_to_token_info(token_info) + self.cache_handler.save_token_to_cache(token_info) + return token_info if as_dict else token_info["access_token"] + except requests.exceptions.HTTPError as http_error: + self._handle_oauth_error(http_error) + + def refresh_access_token(self, refresh_token): + payload = { + "refresh_token": refresh_token, + "grant_type": "refresh_token", + } + + headers = self._make_authorization_headers() + + logger.debug( + "sending POST request to %s with Headers: %s and Body: %r", + self.OAUTH_TOKEN_URL, headers, payload + ) + + try: + response = self._session.post( + self.OAUTH_TOKEN_URL, + data=payload, + headers=headers, + proxies=self.proxies, + timeout=self.requests_timeout, + ) + response.raise_for_status() + token_info = response.json() + token_info = self._add_custom_values_to_token_info(token_info) + if "refresh_token" not in token_info: + token_info["refresh_token"] = refresh_token + self.cache_handler.save_token_to_cache(token_info) + return token_info + except requests.exceptions.HTTPError as http_error: + self._handle_oauth_error(http_error) + + def _add_custom_values_to_token_info(self, token_info): + """ + Store some values that aren't directly provided by a Web API + response. + """ + token_info["expires_at"] = int(time.time()) + token_info["expires_in"] + token_info["scope"] = self.scope + return token_info + + def get_cached_token(self): + warnings.warn("Calling get_cached_token directly on the SpotifyOAuth object will be " + + "deprecated. Instead, please specify a CacheFileHandler instance as " + + "the cache_handler in SpotifyOAuth and use the CacheFileHandler's " + + "get_cached_token method. You can replace:\n\tsp.get_cached_token()" + + "\n\nWith:\n\tsp.validate_token(sp.cache_handler.get_cached_token())", + DeprecationWarning + ) + return self.validate_token(self.cache_handler.get_cached_token()) + + def _save_token_info(self, token_info): + warnings.warn("Calling _save_token_info directly on the SpotifyOAuth object will be " + + "deprecated. Instead, please specify a CacheFileHandler instance as " + + "the cache_handler in SpotifyOAuth and use the CacheFileHandler's " + + "save_token_to_cache method.", + DeprecationWarning + ) + self.cache_handler.save_token_to_cache(token_info) + return None + + +class SpotifyPKCE(SpotifyAuthBase): + """ Implements PKCE Authorization Flow for client apps + + This auth manager enables *user and non-user* endpoints with only + a client ID, redirect URI, and username. When the app requests + an access token for the first time, the user is prompted to + authorize the new client app. After authorizing the app, the client + app is then given both access and refresh tokens. This is the + preferred way of authorizing a mobile/desktop client. + + """ + + OAUTH_AUTHORIZE_URL = "https://accounts.spotify.com/authorize" + OAUTH_TOKEN_URL = "https://accounts.spotify.com/api/token" + + def __init__(self, + client_id=None, + redirect_uri=None, + state=None, + scope=None, + cache_path=None, + username=None, + proxies=None, + requests_timeout=None, + requests_session=True, + open_browser=True, + cache_handler=None): + """ + Creates Auth Manager with the PKCE Auth flow. + + Parameters: + * client_id: Must be supplied or set as environment variable + * redirect_uri: Must be supplied or set as environment variable + * state: Optional, no verification is performed + * scope: Optional, either a list of scopes or comma separated string of scopes. + e.g, "playlist-read-private,playlist-read-collaborative" + * cache_path: (deprecated) Optional, will otherwise be generated + (takes precedence over `username`) + * username: (deprecated) Optional or set as environment variable + (will set `cache_path` to `.cache-{username}`) + * proxies: Optional, proxy for the requests library to route through + * requests_timeout: Optional, tell Requests to stop waiting for a response after + a given number of seconds + * requests_session: A Requests session + * open_browser: Optional, whether or not the web browser should be opened to + authorize a user + * cache_handler: An instance of the `CacheHandler` class to handle + getting and saving cached authorization tokens. + Optional, will otherwise use `CacheFileHandler`. + (takes precedence over `cache_path` and `username`) + """ + + super(SpotifyPKCE, self).__init__(requests_session) + self.client_id = client_id + self.redirect_uri = redirect_uri + self.state = state + self.scope = self._normalize_scope(scope) + if username or cache_path: + warnings.warn("Specifying cache_path or username as arguments to SpotifyPKCE " + + "will be deprecated. Instead, please create a CacheFileHandler " + + "instance with the desired cache_path and username and pass it " + + "to SpotifyPKCE as the cache_handler. For example:\n\n" + + "\tfrom spotipy.oauth2 import CacheFileHandler\n" + + "\thandler = CacheFileHandler(cache_path=cache_path, " + + "username=username)\n" + + "\tsp = spotipy.SpotifyImplicitGrant(client_id, client_secret, " + + "redirect_uri, cache_handler=handler)", + DeprecationWarning + ) + if cache_handler: + warnings.warn("A cache_handler has been specified along with a cache_path or " + + "username. The cache_path and username arguments will be ignored.") + if cache_handler: + assert issubclass(type(cache_handler), CacheHandler), \ + "type(cache_handler): " + str(type(cache_handler)) + " != " + str(CacheHandler) + self.cache_handler = cache_handler + else: + username = (username or os.getenv(CLIENT_CREDS_ENV_VARS["client_username"])) + self.cache_handler = CacheFileHandler( + username=username, + cache_path=cache_path + ) + self.proxies = proxies + self.requests_timeout = requests_timeout + + self._code_challenge_method = "S256" # Spotify requires SHA256 + self.code_verifier = None + self.code_challenge = None + self.authorization_code = None + self.open_browser = open_browser + + def _get_code_verifier(self): + """ Spotify PCKE code verifier - See step 1 of the reference guide below + Reference: + https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce + """ + # Range (33,96) is used to select between 44-128 base64 characters for the + # next operation. The range looks weird because base64 is 6 bytes + import random + length = random.randint(33, 96) + + # The seeded length generates between a 44 and 128 base64 characters encoded string + try: + import secrets + verifier = secrets.token_urlsafe(length) + except ImportError: # For python 3.5 support + import base64 + import os + rand_bytes = os.urandom(length) + verifier = base64.urlsafe_b64encode(rand_bytes).decode('utf-8').replace('=', '') + return verifier + + def _get_code_challenge(self): + """ Spotify PCKE code challenge - See step 1 of the reference guide below + Reference: + https://developer.spotify.com/documentation/general/guides/authorization-guide/#authorization-code-flow-with-proof-key-for-code-exchange-pkce + """ + import base64 + import hashlib + code_challenge_digest = hashlib.sha256(self.code_verifier.encode('utf-8')).digest() + code_challenge = base64.urlsafe_b64encode(code_challenge_digest).decode('utf-8') + return code_challenge.replace('=', '') + + def get_authorize_url(self, state=None): + """ Gets the URL to use to authorize this app """ + if not self.code_challenge: + self.get_pkce_handshake_parameters() + payload = { + "client_id": self.client_id, + "response_type": "code", + "redirect_uri": self.redirect_uri, + "code_challenge_method": self._code_challenge_method, + "code_challenge": self.code_challenge + } + if self.scope: + payload["scope"] = self.scope + if state is None: + state = self.state + if state is not None: + payload["state"] = state + urlparams = urllibparse.urlencode(payload) + return "%s?%s" % (self.OAUTH_AUTHORIZE_URL, urlparams) + + def _open_auth_url(self, state=None): + auth_url = self.get_authorize_url(state) + try: + webbrowser.open(auth_url) + logger.info("Opened %s in your browser", auth_url) + except webbrowser.Error: + logger.error("Please navigate here: %s", auth_url) + + def _get_auth_response(self, open_browser=None): + logger.info('User authentication requires interaction with your ' + 'web browser. Once you enter your credentials and ' + 'give authorization, you will be redirected to ' + 'a url. Paste that url you were directed to to ' + 'complete the authorization.') + + redirect_info = urlparse(self.redirect_uri) + redirect_host, redirect_port = get_host_port(redirect_info.netloc) + + if open_browser is None: + open_browser = self.open_browser + + if ( + open_browser + and redirect_host in ("127.0.0.1", "localhost") + and redirect_info.scheme == "http" + ): + # Only start a local http server if a port is specified + if redirect_port: + return self._get_auth_response_local_server(redirect_port) + else: + logger.warning('Using `%s` as redirect URI without a port. ' + 'Specify a port (e.g. `%s:8080`) to allow ' + 'automatic retrieval of authentication code ' + 'instead of having to copy and paste ' + 'the URL your browser is redirected to.', + redirect_host, redirect_host) + return self._get_auth_response_interactive(open_browser=open_browser) + + def _get_auth_response_local_server(self, redirect_port): + server = start_local_http_server(redirect_port) + self._open_auth_url() + server.handle_request() + + if self.state is not None and server.state != self.state: + raise SpotifyStateError(self.state, server.state) + + if server.auth_code is not None: + return server.auth_code + elif server.error is not None: + raise SpotifyOauthError("Received error from OAuth server: {}".format(server.error)) + else: + raise SpotifyOauthError("Server listening on localhost has not been accessed") + + def _get_auth_response_interactive(self, open_browser=False): + if open_browser or self.open_browser: + self._open_auth_url() + prompt = "Enter the URL you were redirected to: " + else: + url = self.get_authorize_url() + prompt = ( + "Go to the following URL: {}\n" + "Enter the URL you were redirected to: ".format(url) + ) + response = self._get_user_input(prompt) + state, code = self.parse_auth_response_url(response) + if self.state is not None and self.state != state: + raise SpotifyStateError(self.state, state) + return code + + def get_authorization_code(self, response=None): + if response: + return self.parse_response_code(response) + return self._get_auth_response() + + def validate_token(self, token_info): + if token_info is None: + return None + + # if scopes don't match, then bail + if "scope" not in token_info or not self._is_scope_subset( + self.scope, token_info["scope"] + ): + return None + + if self.is_token_expired(token_info): + token_info = self.refresh_access_token( + token_info["refresh_token"] + ) + + return token_info + + def _add_custom_values_to_token_info(self, token_info): + """ + Store some values that aren't directly provided by a Web API + response. + """ + token_info["expires_at"] = int(time.time()) + token_info["expires_in"] + return token_info + + def get_pkce_handshake_parameters(self): + self.code_verifier = self._get_code_verifier() + self.code_challenge = self._get_code_challenge() + + def get_access_token(self, code=None, check_cache=True): + """ Gets the access token for the app + + If the code is not given and no cached token is used, an + authentication window will be shown to the user to get a new + code. + + Parameters: + - code - the response code from authentication + - check_cache - if true, checks for a locally stored token + before requesting a new token + """ + + if check_cache: + token_info = self.validate_token(self.cache_handler.get_cached_token()) + if token_info is not None: + if self.is_token_expired(token_info): + token_info = self.refresh_access_token( + token_info["refresh_token"] + ) + return token_info["access_token"] + + if self.code_verifier is None or self.code_challenge is None: + self.get_pkce_handshake_parameters() + + payload = { + "client_id": self.client_id, + "grant_type": "authorization_code", + "code": code or self.get_authorization_code(), + "redirect_uri": self.redirect_uri, + "code_verifier": self.code_verifier + } + + headers = {"Content-Type": "application/x-www-form-urlencoded"} + + logger.debug( + "sending POST request to %s with Headers: %s and Body: %r", + self.OAUTH_TOKEN_URL, headers, payload + ) + + try: + response = self._session.post( + self.OAUTH_TOKEN_URL, + data=payload, + headers=headers, + verify=True, + proxies=self.proxies, + timeout=self.requests_timeout, + ) + response.raise_for_status() + token_info = response.json() + token_info = self._add_custom_values_to_token_info(token_info) + self.cache_handler.save_token_to_cache(token_info) + return token_info["access_token"] + except requests.exceptions.HTTPError as http_error: + self._handle_oauth_error(http_error) + + def refresh_access_token(self, refresh_token): + payload = { + "refresh_token": refresh_token, + "grant_type": "refresh_token", + "client_id": self.client_id, + } + + headers = {"Content-Type": "application/x-www-form-urlencoded"} + + logger.debug( + "sending POST request to %s with Headers: %s and Body: %r", + self.OAUTH_TOKEN_URL, headers, payload + ) + + try: + response = self._session.post( + self.OAUTH_TOKEN_URL, + data=payload, + headers=headers, + proxies=self.proxies, + timeout=self.requests_timeout, + ) + response.raise_for_status() + token_info = response.json() + token_info = self._add_custom_values_to_token_info(token_info) + if "refresh_token" not in token_info: + token_info["refresh_token"] = refresh_token + self.cache_handler.save_token_to_cache(token_info) + return token_info + except requests.exceptions.HTTPError as http_error: + self._handle_oauth_error(http_error) + + def parse_response_code(self, url): + """ Parse the response code in the given response url + + Parameters: + - url - the response url + """ + _, code = self.parse_auth_response_url(url) + if code is None: + return url + else: + return code + + @staticmethod + def parse_auth_response_url(url): + return SpotifyOAuth.parse_auth_response_url(url) + + def get_cached_token(self): + warnings.warn("Calling get_cached_token directly on the SpotifyPKCE object will be " + + "deprecated. Instead, please specify a CacheFileHandler instance as " + + "the cache_handler in SpotifyOAuth and use the CacheFileHandler's " + + "get_cached_token method. You can replace:\n\tsp.get_cached_token()" + + "\n\nWith:\n\tsp.validate_token(sp.cache_handler.get_cached_token())", + DeprecationWarning + ) + return self.validate_token(self.cache_handler.get_cached_token()) + + def _save_token_info(self, token_info): + warnings.warn("Calling _save_token_info directly on the SpotifyOAuth object will be " + + "deprecated. Instead, please specify a CacheFileHandler instance as " + + "the cache_handler in SpotifyOAuth and use the CacheFileHandler's " + + "save_token_to_cache method.", + DeprecationWarning + ) + self.cache_handler.save_token_to_cache(token_info) + return None + + +class SpotifyImplicitGrant(SpotifyAuthBase): + """ Implements Implicit Grant Flow for client apps + + This auth manager enables *user and non-user* endpoints with only + a client secret, redirect uri, and username. The user will need to + copy and paste a URI from the browser every hour. + + Security Warning + ----------------- + The OAuth standard no longer recommends the Implicit Grant Flow for + client-side code. Spotify has implemented the OAuth-suggested PKCE + extension that removes the need for a client secret in the + Authentication Code flow. Use the SpotifyPKCE auth manager instead + of SpotifyImplicitGrant. + + SpotifyPKCE contains all of the functionality of + SpotifyImplicitGrant, plus automatic response retrieval and + refreshable tokens. Only a few replacements need to be made: + + * get_auth_response()['access_token'] -> + get_access_token(get_authorization_code()) + * get_auth_response() -> + get_access_token(get_authorization_code()); get_cached_token() + * parse_response_token(url)['access_token'] -> + get_access_token(parse_response_code(url)) + * parse_response_token(url) -> + get_access_token(parse_response_code(url)); get_cached_token() + + The security concern in the Implicit Grant flow is that the token is + returned in the URL and can be intercepted through the browser. A + request with an authorization code and proof of origin could not be + easily intercepted without a compromised network. + """ + OAUTH_AUTHORIZE_URL = "https://accounts.spotify.com/authorize" + + def __init__(self, + client_id=None, + redirect_uri=None, + state=None, + scope=None, + cache_path=None, + username=None, + show_dialog=False, + cache_handler=None): + """ Creates Auth Manager using the Implicit Grant flow + + **See help(SpotifyImplicitGrant) for full Security Warning** + + Parameters + ---------- + * client_id: Must be supplied or set as environment variable + * redirect_uri: Must be supplied or set as environment variable + * state: May be supplied, no verification is performed + * scope: Optional, either a list of scopes or comma separated string of scopes. + e.g, "playlist-read-private,playlist-read-collaborative" + * cache_handler: An instance of the `CacheHandler` class to handle + getting and saving cached authorization tokens. + May be supplied, will otherwise use `CacheFileHandler`. + (takes precedence over `cache_path` and `username`) + * cache_path: (deprecated) May be supplied, will otherwise be generated + (takes precedence over `username`) + * username: (deprecated) May be supplied or set as environment variable + (will set `cache_path` to `.cache-{username}`) + * show_dialog: Interpreted as boolean + """ + logger.warning("The OAuth standard no longer recommends the Implicit " + "Grant Flow for client-side code. Use the SpotifyPKCE " + "auth manager instead of SpotifyImplicitGrant. For " + "more details and a guide to switching, see " + "help(SpotifyImplicitGrant).") + + self.client_id = client_id + self.redirect_uri = redirect_uri + self.state = state + if username or cache_path: + warnings.warn("Specifying cache_path or username as arguments to " + + "SpotifyImplicitGrant will be deprecated. Instead, please create " + + "a CacheFileHandler instance with the desired cache_path and " + + "username and pass it to SpotifyImplicitGrant as the " + + "cache_handler. For example:\n\n" + + "\tfrom spotipy.oauth2 import CacheFileHandler\n" + + "\thandler = CacheFileHandler(cache_path=cache_path, " + + "username=username)\n" + + "\tsp = spotipy.SpotifyImplicitGrant(client_id, client_secret, " + + "redirect_uri, cache_handler=handler)", + DeprecationWarning + ) + if cache_handler: + warnings.warn("A cache_handler has been specified along with a cache_path or " + + "username. The cache_path and username arguments will be ignored.") + if cache_handler: + assert issubclass(type(cache_handler), CacheHandler), \ + "type(cache_handler): " + str(type(cache_handler)) + " != " + str(CacheHandler) + self.cache_handler = cache_handler + else: + username = (username or os.getenv(CLIENT_CREDS_ENV_VARS["client_username"])) + self.cache_handler = CacheFileHandler( + username=username, + cache_path=cache_path + ) + self.scope = self._normalize_scope(scope) + self.show_dialog = show_dialog + self._session = None # As to not break inherited __del__ + + def validate_token(self, token_info): + if token_info is None: + return None + + # if scopes don't match, then bail + if "scope" not in token_info or not self._is_scope_subset( + self.scope, token_info["scope"] + ): + return None + + if self.is_token_expired(token_info): + return None + + return token_info + + def get_access_token(self, + state=None, + response=None, + check_cache=True): + """ Gets Auth Token from cache (preferred) or user interaction + + Parameters + ---------- + * state: May be given, overrides (without changing) self.state + * response: URI with token, can break expiration checks + * check_cache: Interpreted as boolean + """ + if check_cache: + token_info = self.validate_token(self.cache_handler.get_cached_token()) + if not (token_info is None or self.is_token_expired(token_info)): + return token_info["access_token"] + + if response: + token_info = self.parse_response_token(response) + else: + token_info = self.get_auth_response(state) + token_info = self._add_custom_values_to_token_info(token_info) + self.cache_handler.save_token_to_cache(token_info) + + return token_info["access_token"] + + def get_authorize_url(self, state=None): + """ Gets the URL to use to authorize this app """ + payload = { + "client_id": self.client_id, + "response_type": "token", + "redirect_uri": self.redirect_uri, + } + if self.scope: + payload["scope"] = self.scope + if state is None: + state = self.state + if state is not None: + payload["state"] = state + if self.show_dialog: + payload["show_dialog"] = True + + urlparams = urllibparse.urlencode(payload) + + return "%s?%s" % (self.OAUTH_AUTHORIZE_URL, urlparams) + + def parse_response_token(self, url, state=None): + """ Parse the response code in the given response url """ + remote_state, token, t_type, exp_in = self.parse_auth_response_url(url) + if state is None: + state = self.state + if state is not None and remote_state != state: + raise SpotifyStateError(state, remote_state) + return {"access_token": token, "token_type": t_type, + "expires_in": exp_in, "state": state} + + @staticmethod + def parse_auth_response_url(url): + url_components = urlparse(url) + fragment_s = url_components.fragment + query_s = url_components.query + form = dict(i.split('=') for i + in (fragment_s or query_s or url).split('&')) + if "error" in form: + raise SpotifyOauthError("Received error from auth server: " + "{}".format(form["error"]), + state=form["state"]) + if "expires_in" in form: + form["expires_in"] = int(form["expires_in"]) + return tuple(form.get(param) for param in ["state", "access_token", + "token_type", "expires_in"]) + + def _open_auth_url(self, state=None): + auth_url = self.get_authorize_url(state) + try: + webbrowser.open(auth_url) + logger.info("Opened %s in your browser", auth_url) + except webbrowser.Error: + logger.error("Please navigate here: %s", auth_url) + + def get_auth_response(self, state=None): + """ Gets a new auth **token** with user interaction """ + logger.info('User authentication requires interaction with your ' + 'web browser. Once you enter your credentials and ' + 'give authorization, you will be redirected to ' + 'a url. Paste that url you were directed to to ' + 'complete the authorization.') + + redirect_info = urlparse(self.redirect_uri) + redirect_host, redirect_port = get_host_port(redirect_info.netloc) + # Implicit Grant tokens are returned in a hash fragment + # which is only available to the browser. Therefore, interactive + # URL retrieval is required. + if (redirect_host in ("127.0.0.1", "localhost") + and redirect_info.scheme == "http" and redirect_port): + logger.warning('Using a local redirect URI with a ' + 'port, likely expecting automatic ' + 'retrieval. Due to technical limitations, ' + 'the authentication token cannot be ' + 'automatically retrieved and must be ' + 'copied and pasted.') + + self._open_auth_url(state) + logger.info('Paste that url you were directed to in order to ' + 'complete the authorization') + response = SpotifyImplicitGrant._get_user_input("Enter the URL you " + "were redirected to: ") + return self.parse_response_token(response, state) + + def _add_custom_values_to_token_info(self, token_info): + """ + Store some values that aren't directly provided by a Web API + response. + """ + token_info["expires_at"] = int(time.time()) + token_info["expires_in"] + token_info["scope"] = self.scope + return token_info + + def get_cached_token(self): + warnings.warn("Calling get_cached_token directly on the SpotifyImplicitGrant " + + "object will be deprecated. Instead, please specify a " + + "CacheFileHandler instance as the cache_handler in SpotifyOAuth " + + "and use the CacheFileHandler's get_cached_token method. " + + "You can replace:\n\tsp.get_cached_token()" + + "\n\nWith:\n\tsp.validate_token(sp.cache_handler.get_cached_token())", + DeprecationWarning + ) + return self.validate_token(self.cache_handler.get_cached_token()) + + def _save_token_info(self, token_info): + warnings.warn("Calling _save_token_info directly on the SpotifyImplicitGrant " + + "object will be deprecated. Instead, please specify a " + + "CacheFileHandler instance as the cache_handler in SpotifyOAuth " + + "and use the CacheFileHandler's save_token_to_cache method.", + DeprecationWarning + ) + self.cache_handler.save_token_to_cache(token_info) + return None + + +class RequestHandler(BaseHTTPRequestHandler): + def do_GET(self): + self.server.auth_code = self.server.error = None + try: + state, auth_code = SpotifyOAuth.parse_auth_response_url(self.path) + self.server.state = state + self.server.auth_code = auth_code + except SpotifyOauthError as error: + self.server.error = error + + self.send_response(200) + self.send_header("Content-Type", "text/html") + self.end_headers() + + if self.server.auth_code: + status = "successful" + elif self.server.error: + status = "failed ({})".format(self.server.error) + else: + self._write("

Invalid request

") + return + + self._write(""" + + +

Authentication status: {}

+This window can be closed. + + + +""".format(status)) + + def _write(self, text): + return self.wfile.write(text.encode("utf-8")) + + def log_message(self, format, *args): + return + + +def start_local_http_server(port, handler=RequestHandler): + server = HTTPServer(("127.0.0.1", port), handler) + server.allow_reuse_address = True + server.auth_code = None + server.auth_token_form = None + server.error = None + return server diff --git a/resources/lib/deps/spotipy/util.py b/resources/lib/deps/spotipy/util.py new file mode 100644 index 0000000..b949a61 --- /dev/null +++ b/resources/lib/deps/spotipy/util.py @@ -0,0 +1,135 @@ +# -*- coding: utf-8 -*- + +""" Shows a user's playlists (need to be authenticated via oauth) """ + +__all__ = ["CLIENT_CREDS_ENV_VARS", "prompt_for_user_token"] + +import logging +import os +import warnings + +import spotipy + +LOGGER = logging.getLogger(__name__) + +CLIENT_CREDS_ENV_VARS = { + "client_id": "SPOTIPY_CLIENT_ID", + "client_secret": "SPOTIPY_CLIENT_SECRET", + "client_username": "SPOTIPY_CLIENT_USERNAME", + "redirect_uri": "SPOTIPY_REDIRECT_URI", +} + + +def prompt_for_user_token( + username=None, + scope=None, + client_id=None, + client_secret=None, + redirect_uri=None, + cache_path=None, + oauth_manager=None, + show_dialog=False +): + warnings.warn( + "'prompt_for_user_token' is deprecated." + "Use the following instead: " + " auth_manager=SpotifyOAuth(scope=scope)" + " spotipy.Spotify(auth_manager=auth_manager)", + DeprecationWarning + ) + """ prompts the user to login if necessary and returns + the user token suitable for use with the spotipy.Spotify + constructor + + Parameters: + + - username - the Spotify username (optional) + - scope - the desired scope of the request (optional) + - client_id - the client id of your app (required) + - client_secret - the client secret of your app (required) + - redirect_uri - the redirect URI of your app (required) + - cache_path - path to location to save tokens (optional) + - oauth_manager - Oauth manager object (optional) + - show_dialog - If true, a login prompt always shows (optional, defaults to False) + + """ + if not oauth_manager: + if not client_id: + client_id = os.getenv("SPOTIPY_CLIENT_ID") + + if not client_secret: + client_secret = os.getenv("SPOTIPY_CLIENT_SECRET") + + if not redirect_uri: + redirect_uri = os.getenv("SPOTIPY_REDIRECT_URI") + + if not client_id: + LOGGER.warning( + """ + You need to set your Spotify API credentials. + You can do this by setting environment variables like so: + + export SPOTIPY_CLIENT_ID='your-spotify-client-id' + export SPOTIPY_CLIENT_SECRET='your-spotify-client-secret' + export SPOTIPY_REDIRECT_URI='your-app-redirect-url' + + Get your credentials at + https://developer.spotify.com/my-applications + """ + ) + raise spotipy.SpotifyException(550, -1, "no credentials set") + + sp_oauth = oauth_manager or spotipy.SpotifyOAuth( + client_id, + client_secret, + redirect_uri, + scope=scope, + cache_path=cache_path, + username=username, + show_dialog=show_dialog + ) + + # try to get a valid token for this user, from the cache, + # if not in the cache, then create a new (this will send + # the user to a web page where they can authorize this app) + + token_info = sp_oauth.validate_token(sp_oauth.cache_handler.get_cached_token()) + + if not token_info: + code = sp_oauth.get_auth_response() + token = sp_oauth.get_access_token(code, as_dict=False) + else: + return token_info["access_token"] + + # Auth'ed API request + if token: + return token + else: + return None + + +def get_host_port(netloc): + if ":" in netloc: + host, port = netloc.split(":", 1) + port = int(port) + else: + host = netloc + port = None + + return host, port + + +def normalize_scope(scope): + if scope: + if isinstance(scope, str): + scopes = scope.split(',') + elif isinstance(scope, list) or isinstance(scope, tuple): + scopes = scope + else: + raise Exception( + "Unsupported scope value, please either provide a list of scopes, " + "or a string of scopes separated by commas" + ) + return " ".join(sorted(scopes)) + else: + return None diff --git a/resources/lib/http_video_player_setter.py b/resources/lib/http_video_player_setter.py new file mode 100644 index 0000000..67f8800 --- /dev/null +++ b/resources/lib/http_video_player_setter.py @@ -0,0 +1,84 @@ +import os +from xml.etree import ElementTree + +import xbmcvfs +from xbmc import LOGDEBUG + +from utils import ADDON_ID, log_msg + + +class HttpVideoPlayerSetter: + def __init__(self): + self.__plugin_name = ADDON_ID + self.__player_rules_filename = xbmcvfs.translatePath( + f"special://masterprofile/playercorefactory.xml" + ) + + def set_http_rule(self) -> bool: + if not os.path.exists(self.__player_rules_filename): + self.__create_new_player_rules() + log_msg(f"Created a new file '{self.__player_rules_filename}' with a video http rule.") + return True + else: + if self.__add_http_rule(): + log_msg(f"Added a video http rule to '{self.__player_rules_filename}'") + return True + + log_msg( + f"There is already a video http rule in '{self.__player_rules_filename}'." + " Nothing to do.", + LOGDEBUG, + ) + return False + + def __create_new_player_rules(self) -> None: + xml_str = f""" + + + + + + +""" + with open(self.__player_rules_filename, "w") as f: + f.write(xml_str) + + def __add_http_rule(self) -> bool: + class CommentedTreeBuilder(ElementTree.TreeBuilder): + def comment(self, data): + self.start(ElementTree.Comment().tag, {}) + self.data(data) + self.end(ElementTree.Comment().tag) + + parser = ElementTree.XMLParser(target=CommentedTreeBuilder()) + tree = ElementTree.parse(self.__player_rules_filename, parser=parser) + root = tree.getroot() + + http_rule = root.findall("./rules/rule/[@protocols='http']") + if http_rule: + return False + + rules = root.find("./rules") + + attributes = { + "name": "http", + "protocols": "http", + "player": "VideoPlayer", + } + new_rule = ElementTree.Element("rule", attributes) + new_rule.tail = "\n\n" + " " + rules.insert(0, new_rule) + + comment = ElementTree.Comment( + f" This http rule added by the '{self.__plugin_name}' addon. " + ) + comment.tail = "\n" + " " + rules.insert(0, comment) + + xml_str = ElementTree.tostring(root, encoding="unicode", xml_declaration=True) + + with open(self.__player_rules_filename, "w") as f: + f.write(xml_str) + f.write("\n") + + return True diff --git a/resources/lib/librespot/__init__.py b/resources/lib/librespot/__init__.py new file mode 100644 index 0000000..9503e25 --- /dev/null +++ b/resources/lib/librespot/__init__.py @@ -0,0 +1,34 @@ +from __future__ import annotations +from librespot.crypto import DiffieHellman +from librespot.proto.Keyexchange_pb2 import BuildInfo, Platform, Product, ProductFlags +from librespot.structure import Closeable, Runnable +import platform + + +class Version: + version_name = "0.0.9" + + @staticmethod + def platform() -> Platform: + if platform.system() == "Windows": + return Platform.PLATFORM_WIN32_X86 + if platform.system() == "Darwin": + return Platform.PLATFORM_OSX_X86 + return Platform.PLATFORM_LINUX_X86 + + @staticmethod + def version_string(): + return "librespot-python " + Version.version_name + + @staticmethod + def system_info_string(): + return Version.version_string() + \ + "; Python " + platform.python_version() + \ + "; " + platform.system() + + @staticmethod + def standard_build_info() -> BuildInfo: + return BuildInfo(product=Product.PRODUCT_CLIENT, + product_flags=[ProductFlags.PRODUCT_FLAG_NONE], + platform=Version.platform(), + version=117300517) diff --git a/resources/lib/librespot/audio/__init__.py b/resources/lib/librespot/audio/__init__.py new file mode 100644 index 0000000..423c59c --- /dev/null +++ b/resources/lib/librespot/audio/__init__.py @@ -0,0 +1,912 @@ +from __future__ import annotations +from librespot import util +from librespot.audio.decrypt import AesAudioDecrypt +from librespot.audio.format import SuperAudioFormat +from librespot.audio.storage import ChannelManager +from librespot.cache import CacheManager +from librespot.crypto import Packet +from librespot.metadata import EpisodeId, PlayableId, TrackId +from librespot.proto import Metadata_pb2 as Metadata, StorageResolve_pb2 as StorageResolve +from librespot.structure import AudioDecrypt, AudioQualityPicker, Closeable, FeederException, GeneralAudioStream, GeneralWritableStream, HaltListener, NoopAudioDecrypt, PacketsReceiver +import concurrent.futures +import io +import logging +import math +import queue +import random +import struct +import threading +import time +import typing +import urllib.parse + +if typing.TYPE_CHECKING: + from librespot.core import Session + + +class AbsChunkedInputStream(io.BytesIO, HaltListener): + chunk_exception = None + closed = False + max_chunk_tries = 128 + preload_ahead = 3 + preload_chunk_retries = 2 + retries: typing.List[int] + retry_on_chunk_error: bool + wait_lock: threading.Condition = threading.Condition() + wait_for_chunk = -1 + __decoded_length = 0 + __mark = 0 + __pos = 0 + + def __init__(self, retry_on_chunk_error: bool): + super().__init__() + self.retries = [0] * self.chunks() + self.retry_on_chunk_error = retry_on_chunk_error + + def is_closed(self) -> bool: + return self.closed + + def buffer(self) -> typing.List[bytes]: + raise NotImplementedError() + + def size(self) -> int: + raise NotImplementedError() + + def close(self) -> None: + self.closed = True + with self.wait_lock: + self.wait_lock.notify_all() + + def available(self): + return self.size() - self.__pos + + def mark_supported(self) -> bool: + return True + + def mark(self, read_ahead_limit: int) -> None: + self.__mark = self.__pos + + def reset(self) -> None: + self.__pos = self.__mark + + def pos(self) -> int: + return self.__pos + + def seek(self, where: int, **kwargs) -> None: + if where < 0: + raise TypeError() + if self.closed: + raise IOError("Stream is closed!") + self.__pos = where + self.check_availability(int(self.__pos / (128 * 1024)), False, False) + + def skip(self, n: int) -> int: + if n < 0: + raise TypeError() + if self.closed: + raise IOError("Stream is closed!") + k = self.size() - self.__pos + if n < k: + k = n + self.__pos += k + chunk = int(self.__pos / (128 * 1024)) + self.check_availability(chunk, False, False) + return k + + def requested_chunks(self) -> typing.List[bool]: + raise NotImplementedError() + + def available_chunks(self) -> typing.List[bool]: + raise NotImplementedError() + + def chunks(self) -> int: + raise NotImplementedError() + + def request_chunk_from_stream(self, index: int) -> None: + raise NotImplementedError() + + def should_retry(self, chunk: int) -> bool: + if self.retries[chunk] < 1: + return True + if self.retries[chunk] > self.max_chunk_tries: + return False + return self.retry_on_chunk_error + + def check_availability(self, chunk: int, wait: bool, halted: bool) -> None: + if halted and not wait: + raise TypeError() + if not self.requested_chunks()[chunk]: + self.request_chunk_from_stream(chunk) + self.requested_chunks()[chunk] = True + for i in range(chunk + 1, + min(self.chunks() - 1, chunk + self.preload_ahead) + 1): + if (self.requested_chunks()[i] + and self.retries[i] < self.preload_chunk_retries): + self.request_chunk_from_stream(i) + self.requested_chunks()[chunk] = True + if wait: + if self.available_chunks()[chunk]: + return + retry = False + with self.wait_lock: + if not halted: + self.stream_read_halted(chunk, int(time.time() * 1000)) + self.chunk_exception = None + self.wait_for_chunk = chunk + self.wait_lock.wait_for(lambda: self.available_chunks()[chunk]) + if self.closed: + return + if self.chunk_exception is not None: + if self.should_retry(chunk): + retry = True + else: + raise AbsChunkedInputStream.ChunkException + if not retry: + self.stream_read_halted(chunk, int(time.time() * 1000)) + if retry: + time.sleep(math.log10(self.retries[chunk])) + self.check_availability(chunk, True, True) + + def read(self, __size: int = 0) -> bytes: + if self.closed: + raise IOError("Stream is closed!") + if __size <= 0: + if self.__pos == self.size(): + return b"" + buffer = io.BytesIO() + total_size = self.size() + chunk = int(self.__pos / (128 * 1024)) + chunk_off = int(self.__pos % (128 * 1024)) + chunk_total = int(math.ceil(total_size / (128 * 1024))) + self.check_availability(chunk, True, False) + buffer.write(self.buffer()[chunk][chunk_off:]) + chunk += 1 + if chunk != chunk_total: + while chunk <= chunk_total - 1: + self.check_availability(chunk, True, False) + buffer.write(self.buffer()[chunk]) + chunk += 1 + buffer.seek(0) + self.__pos += buffer.getbuffer().nbytes + return buffer.read() + buffer = io.BytesIO() + chunk = int(self.__pos / (128 * 1024)) + chunk_off = int(self.__pos % (128 * 1024)) + chunk_end = int(__size / (128 * 1024)) + chunk_end_off = int(__size % (128 * 1024)) + if chunk_end > self.size(): + chunk_end = int(self.size() / (128 * 1024)) + chunk_end_off = int(self.size() % (128 * 1024)) + self.check_availability(chunk, True, False) + if chunk_off + __size > len(self.buffer()[chunk]): + buffer.write(self.buffer()[chunk][chunk_off:]) + chunk += 1 + while chunk <= chunk_end: + self.check_availability(chunk, True, False) + if chunk == chunk_end: + buffer.write(self.buffer()[chunk][:chunk_end_off]) + else: + buffer.write(self.buffer()[chunk]) + chunk += 1 + else: + buffer.write(self.buffer()[chunk][chunk_off:chunk_off + __size]) + buffer.seek(0) + self.__pos += buffer.getbuffer().nbytes + return buffer.read() + + def notify_chunk_available(self, index: int) -> None: + self.available_chunks()[index] = True + self.__decoded_length += len(self.buffer()[index]) + with self.wait_lock: + if index == self.wait_for_chunk and not self.closed: + self.wait_for_chunk = -1 + self.wait_lock.notify_all() + + def notify_chunk_error(self, index: int, ex): + self.available_chunks()[index] = False + self.requested_chunks()[index] = False + self.retries[index] += 1 + with self.wait_lock: + if index == self.wait_for_chunk and not self.closed: + self.chunk_exception = ex + self.wait_for_chunk = -1 + self.wait_lock.notify_all() + + def decoded_length(self): + return self.__decoded_length + + class ChunkException(IOError): + + @staticmethod + def from_stream_error(stream_error: int): + return AbsChunkedInputStream \ + .ChunkException("Failed due to stream error, code: {}".format(stream_error)) + + +class AudioKeyManager(PacketsReceiver, Closeable): + audio_key_request_timeout = 20 + logger = logging.getLogger("Librespot:AudioKeyManager") + __callbacks: typing.Dict[int, Callback] = {} + __seq_holder = 0 + __seq_holder_lock = threading.Condition() + __session: Session + __zero_short = b"\x00\x00" + + def __init__(self, session: Session): + self.__session = session + + def dispatch(self, packet: Packet) -> None: + payload = io.BytesIO(packet.payload) + seq = struct.unpack(">i", payload.read(4))[0] + callback = self.__callbacks.get(seq) + if callback is None: + self.logger.warning( + "Couldn't find callback for seq: {}".format(seq)) + return + if packet.is_cmd(Packet.Type.aes_key): + key = payload.read(16) + callback.key(key) + elif packet.is_cmd(Packet.Type.aes_key_error): + code = struct.unpack(">H", payload.read(2))[0] + callback.error(code) + else: + self.logger.warning( + "Couldn't handle packet, cmd: {}, length: {}".format( + packet.cmd, len(packet.payload))) + + def get_audio_key(self, + gid: bytes, + file_id: bytes, + retry: bool = True) -> bytes: + seq: int + with self.__seq_holder_lock: + seq = self.__seq_holder + self.__seq_holder += 1 + out = io.BytesIO() + out.write(file_id) + out.write(gid) + out.write(struct.pack(">i", seq)) + out.write(self.__zero_short) + out.seek(0) + self.__session.send(Packet.Type.request_key, out.read()) + callback = AudioKeyManager.SyncCallback(self) + self.__callbacks[seq] = callback + key = callback.wait_response() + if key is None: + if retry: + return self.get_audio_key(gid, file_id, False) + raise RuntimeError( + "Failed fetching audio key! gid: {}, fileId: {}".format( + util.bytes_to_hex(gid), util.bytes_to_hex(file_id))) + return key + + class Callback: + + def key(self, key: bytes) -> None: + raise NotImplementedError + + def error(self, code: int) -> None: + raise NotImplementedError + + class SyncCallback(Callback): + __audio_key_manager: AudioKeyManager + __reference = queue.Queue() + __reference_lock = threading.Condition() + + def __init__(self, audio_key_manager: AudioKeyManager): + self.__audio_key_manager = audio_key_manager + + def key(self, key: bytes) -> None: + with self.__reference_lock: + self.__reference.put(key) + self.__reference_lock.notify_all() + + def error(self, code: int) -> None: + self.__audio_key_manager.logger.fatal( + "Audio key error, code: {}".format(code)) + with self.__reference_lock: + self.__reference.put(None) + self.__reference_lock.notify_all() + + def wait_response(self) -> bytes: + with self.__reference_lock: + self.__reference_lock.wait( + AudioKeyManager.audio_key_request_timeout) + return self.__reference.get(block=False) + + +class CdnFeedHelper: + _LOGGER: logging = logging.getLogger(__name__) + + @staticmethod + def get_url(resp: StorageResolve.StorageResolveResponse) -> str: + selected_url = random.choice(resp.cdnurl) + while "audio4-gm-fb" in selected_url or "audio-gm-fb" in selected_url: + selected_url = random.choice(resp.cdnurl) + return selected_url + + @staticmethod + def load_track( + session: Session, track: Metadata.Track, file: Metadata.AudioFile, + resp_or_url: typing.Union[StorageResolve.StorageResolveResponse, + str], preload: bool, + halt_listener: HaltListener) -> PlayableContentFeeder.LoadedStream: + if type(resp_or_url) is str: + url = resp_or_url + else: + url = CdnFeedHelper.get_url(resp_or_url) + start = int(time.time() * 1000) + key = session.audio_key().get_audio_key(track.gid, file.file_id) + audio_key_time = int(time.time() * 1000) - start + + streamer = session.cdn().stream_file(file, key, url, halt_listener) + input_stream = streamer.stream() + normalization_data = NormalizationData.read(input_stream) + if input_stream.skip(0xA7) != 0xA7: + raise IOError("Couldn't skip 0xa7 bytes!") + return PlayableContentFeeder.LoadedStream( + track, + streamer, + normalization_data, + PlayableContentFeeder.Metrics(file.file_id, preload, + -1 if preload else audio_key_time), + ) + + @staticmethod + def load_episode_external( + session: Session, episode: Metadata.Episode, + halt_listener: HaltListener) -> PlayableContentFeeder.LoadedStream: + resp = session.client().head(episode.external_url) + + if resp.status_code != 200: + CdnFeedHelper._LOGGER.warning("Couldn't resolve redirect!") + + url = resp.url + CdnFeedHelper._LOGGER.debug("Fetched external url for {}: {}".format( + util.bytes_to_hex(episode.gid), url)) + + streamer = session.cdn().stream_external_episode( + episode, url, halt_listener) + return PlayableContentFeeder.LoadedStream( + episode, + streamer, + None, + PlayableContentFeeder.Metrics(None, False, -1), + ) + + @staticmethod + def load_episode( + session: Session, + episode: Metadata.Episode, + file: Metadata.AudioFile, + resp_or_url: typing.Union[StorageResolve.StorageResolveResponse, str], + preload: bool, + halt_listener: HaltListener, + ) -> PlayableContentFeeder.LoadedStream: + if type(resp_or_url) is str: + url = resp_or_url + else: + url = CdnFeedHelper.get_url(resp_or_url) + start = int(time.time() * 1000) + key = session.audio_key().get_audio_key(episode.gid, file.file_id) + audio_key_time = int(time.time() * 1000) - start + + streamer = session.cdn().stream_file(file, key, url, halt_listener) + input_stream = streamer.stream() + normalization_data = NormalizationData.read(input_stream) + if input_stream.skip(0xA7) != 0xA7: + raise IOError("Couldn't skip 0xa7 bytes!") + return PlayableContentFeeder.LoadedStream( + episode, + streamer, + normalization_data, + PlayableContentFeeder.Metrics(file.file_id, preload, + -1 if preload else audio_key_time), + ) + + +class CdnManager: + logger: logging = logging.getLogger("Librespot:CdnManager") + __session: Session + + def __init__(self, session: Session): + self.__session = session + + def get_head(self, file_id: bytes): + response = self.__session.client() \ + .get(self.__session.get_user_attribute("head-files-url", "https://heads-fa.spotify.com/head/{file_id}") + .replace("{file_id}", util.bytes_to_hex(file_id))) + if response.status_code != 200: + raise IOError("{}".format(response.status_code)) + body = response.content + if body is None: + raise IOError("Response body is empty!") + return body + + def stream_external_episode(self, episode: Metadata.Episode, + external_url: str, + halt_listener: HaltListener): + return CdnManager.Streamer( + self.__session, + StreamId(episode=episode), + SuperAudioFormat.MP3, + CdnManager.CdnUrl(self, None, external_url), + self.__session.cache(), + NoopAudioDecrypt(), + halt_listener, + ) + + def stream_file(self, file: Metadata.AudioFile, key: bytes, url: str, + halt_listener: HaltListener): + return CdnManager.Streamer( + self.__session, + StreamId(file=file), + SuperAudioFormat.get(file.format), + CdnManager.CdnUrl(self, file.file_id, url), + self.__session.cache(), + AesAudioDecrypt(key), + halt_listener, + ) + + def get_audio_url(self, file_id: bytes): + response = self.__session.api()\ + .send("GET", "/storage-resolve/files/audio/interactive/{}".format(util.bytes_to_hex(file_id)), None, None) + if response.status_code != 200: + raise IOError(response.status_code) + body = response.content + if body is None: + raise IOError("Response body is empty!") + proto = StorageResolve.StorageResolveResponse() + proto.ParseFromString(body) + if proto.result == StorageResolve.StorageResolveResponse.Result.CDN: + url = random.choice(proto.cdnurl) + self.logger.debug("Fetched CDN url for {}: {}".format( + util.bytes_to_hex(file_id), url)) + return url + raise CdnManager.CdnException( + "Could not retrieve CDN url! result: {}".format(proto.result)) + + class CdnException(Exception): + pass + + class InternalResponse: + buffer: bytes + headers: typing.Dict[str, str] + + def __init__(self, buffer: bytes, headers: typing.Dict[str, str]): + self.buffer = buffer + self.headers = headers + + class CdnUrl: + __cdn_manager = None + __file_id: bytes + __expiration: int + url: str + + def __init__(self, cdn_manager, file_id: typing.Union[bytes, None], + url: str): + self.__cdn_manager: CdnManager = cdn_manager + self.__file_id = file_id + self.set_url(url) + + def url(self): + if self.__expiration == -1: + return self.url + if self.__expiration <= int(time.time() * 1000) + 5 * 60 * 1000: + self.url = self.__cdn_manager.get_audio_url(self.__file_id) + return self.url + + def set_url(self, url: str): + self.url = url + if self.__file_id is not None: + token_url = urllib.parse.urlparse(url) + token_query = urllib.parse.parse_qs(token_url.query) + token_list = token_query.get("__token__") + try: + token_str = str(token_list[0]) + except TypeError: + token_str = "" + expires_list = token_query.get("Expires") + try: + expires_str = str(expires_list[0]) + except TypeError: + expires_str = "" + if token_str != "None" and len(token_str) != 0: + expire_at = None + split = token_str.split("~") + for s in split: + try: + i = s.index("=") + except ValueError: + continue + if s[:i] == "exp": + expire_at = int(s[i + 1:]) + break + if expire_at is None: + self.__expiration = -1 + self.__cdn_manager.logger.warning( + "Invalid __token__ in CDN url: {}".format(url)) + return + self.__expiration = expire_at * 1000 + elif expires_str != "None" and len(expires_str) != 0: + expires_at = None + expires_str = expires_str.split("~")[0] + expires_at = int(expires_str) + if expires_at is None: + self.__expiration = -1 + self.__cdn_manager.logger.warning("Invalid Expires param in CDN url: {}".format(url)) + return + self.__expiration = expires_at * 1000 + else: + try: + i = token_url.query.index("_") + except ValueError: + self.__expiration = -1 + self.__cdn_manager.logger \ + .warning("Couldn't extract expiration, invalid parameter in CDN url: {}".format(url)) + return + self.__expiration = int(token_url.query[:i]) * 1000 + + else: + self.__expiration = -1 + + class Streamer(GeneralAudioStream, GeneralWritableStream): + available: typing.List[bool] + buffer: typing.List[bytes] + chunks: int + executor_service = concurrent.futures.ThreadPoolExecutor() + halt_listener: HaltListener + requested: typing.List[bool] + size: int + __audio_format: SuperAudioFormat + __audio_decrypt: AudioDecrypt + __cdn_url: CdnManager.CdnUrl + __internal_stream: InternalStream + __session: Session + __stream_id: StreamId + + def __init__(self, session: Session, stream_id: StreamId, + audio_format: SuperAudioFormat, + cdn_url: CdnManager.CdnUrl, cache: CacheManager, + audio_decrypt: AudioDecrypt, halt_listener: HaltListener): + self.__session = session + self.__stream_id = stream_id + self.__audio_format = audio_format + self.__audio_decrypt = audio_decrypt + self.__cdn_url = cdn_url + self.halt_listener = halt_listener + response = self.request(range_start=0, + range_end=ChannelManager.chunk_size - 1) + content_range = response.headers.get("Content-Range") + if content_range is None: + raise IOError("Missing Content-Range header!") + split = content_range.split("/") + self.size = int(split[1]) + self.chunks = int(math.ceil(self.size / ChannelManager.chunk_size)) + first_chunk = response.buffer + self.available = [False for _ in range(self.chunks)] + self.requested = [False for _ in range(self.chunks)] + self.buffer = [b"" for _ in range(self.chunks)] + self.__internal_stream = CdnManager.Streamer.InternalStream( + self, False) + self.requested[0] = True + self.write_chunk(first_chunk, 0, False) + + def write_chunk(self, chunk: bytes, chunk_index: int, + cached: bool) -> None: + if self.__internal_stream.is_closed(): + return + self.__session.logger.debug( + "Chunk {}/{} completed, cached: {}, stream: {}".format( + chunk_index + 1, self.chunks, cached, self.describe())) + self.buffer[chunk_index] = self.__audio_decrypt.decrypt_chunk( + chunk_index, chunk) + self.__internal_stream.notify_chunk_available(chunk_index) + + def stream(self) -> AbsChunkedInputStream: + return self.__internal_stream + + def codec(self) -> SuperAudioFormat: + return self.__audio_format + + def describe(self) -> str: + if self.__stream_id.is_episode(): + return "episode_gid: {}".format( + self.__stream_id.get_episode_gid()) + return "file_id: {}".format(self.__stream_id.get_file_id()) + + def decrypt_time_ms(self) -> int: + return self.__audio_decrypt.decrypt_time_ms() + + def request_chunk(self, index: int) -> None: + response = self.request(index) + self.write_chunk(response.buffer, index, False) + + def request(self, chunk: int = None, range_start: int = None, range_end: int = None)\ + -> CdnManager.InternalResponse: + if chunk is None and range_start is None and range_end is None: + raise TypeError() + if chunk is not None: + range_start = ChannelManager.chunk_size * chunk + range_end = (chunk + 1) * ChannelManager.chunk_size - 1 + response = self.__session.client().get( + self.__cdn_url.url, + headers={ + "Range": "bytes={}-{}".format(range_start, range_end) + }, + ) + if response.status_code != 206: + raise IOError(response.status_code) + body = response.content + if body is None: + raise IOError("Response body is empty!") + return CdnManager.InternalResponse(body, dict(response.headers)) + + class InternalStream(AbsChunkedInputStream): + streamer: CdnManager.Streamer + + def __init__(self, streamer, retry_on_chunk_error: bool): + self.streamer: CdnManager.Streamer = streamer + super().__init__(retry_on_chunk_error) + + def buffer(self) -> typing.List[bytes]: + return self.streamer.buffer + + def size(self) -> int: + return self.streamer.size + + def close(self) -> None: + super().close() + del self.streamer.buffer + + def requested_chunks(self) -> typing.List[bool]: + return self.streamer.requested + + def available_chunks(self) -> typing.List[bool]: + return self.streamer.available + + def chunks(self) -> int: + return self.streamer.chunks + + def request_chunk_from_stream(self, index: int) -> None: + self.streamer.executor_service \ + .submit(lambda: self.streamer.request_chunk(index)) + + def stream_read_halted(self, chunk: int, _time: int) -> None: + if self.streamer.halt_listener is not None: + self.streamer.executor_service\ + .submit(lambda: self.streamer.halt_listener.stream_read_halted(chunk, _time)) + + def stream_read_resumed(self, chunk: int, _time: int) -> None: + if self.streamer.halt_listener is not None: + self.streamer.executor_service \ + .submit(lambda: self.streamer.halt_listener.stream_read_resumed(chunk, _time)) + + +class NormalizationData: + _LOGGER: logging = logging.getLogger(__name__) + track_gain_db: float + track_peak: float + album_gain_db: float + album_peak: float + + def __init__(self, track_gain_db: float, track_peak: float, + album_gain_db: float, album_peak: float): + self.track_gain_db = track_gain_db + self.track_peak = track_peak + self.album_gain_db = album_gain_db + self.album_peak = album_peak + + self._LOGGER.debug( + "Loaded normalization data, track_gain: {}, track_peak: {}, album_gain: {}, album_peak: {}" + .format(track_gain_db, track_peak, album_gain_db, album_peak)) + + @staticmethod + def read(input_stream: AbsChunkedInputStream) -> NormalizationData: + input_stream.seek(144) + data = input_stream.read(4 * 4) + input_stream.seek(0) + buffer = io.BytesIO(data) + return NormalizationData( + struct.unpack(" float: + normalisation_factor = float( + math.pow(10, (self.track_gain_db + normalisation_pregain) / 20)) + if normalisation_factor * self.track_peak > 1: + self._LOGGER \ + .warning("Reducing normalisation factor to prevent clipping. Please add negative pregain to avoid.") + normalisation_factor = 1 / self.track_peak + return normalisation_factor + + +class PlayableContentFeeder: + logger = logging.getLogger("Librespot:PlayableContentFeeder") + storage_resolve_interactive = "/storage-resolve/files/audio/interactive/{}" + storage_resolve_interactive_prefetch = "/storage-resolve/files/audio/interactive_prefetch/{}" + __session: Session + + def __init__(self, session: Session): + self.__session = session + + def load(self, playable_id: PlayableId, + audio_quality_picker: AudioQualityPicker, preload: bool, + halt_listener: typing.Union[HaltListener, None]): + if type(playable_id) is TrackId: + return self.load_track(playable_id, audio_quality_picker, preload, + halt_listener) + if type(playable_id) is EpisodeId: + return self.load_episode(playable_id, audio_quality_picker, + preload, halt_listener) + raise TypeError("Unknown content: {}".format(playable_id)) + + def load_stream(self, file: Metadata.AudioFile, track: Metadata.Track, + episode: Metadata.Episode, preload: bool, + halt_lister: HaltListener): + if track is None and episode is None: + raise RuntimeError() + response = self.resolve_storage_interactive(file.file_id, preload) + if response.result == StorageResolve.StorageResolveResponse.Result.CDN: + if track is not None: + return CdnFeedHelper.load_track(self.__session, track, file, + response, preload, halt_lister) + return CdnFeedHelper.load_episode(self.__session, episode, file, + response, preload, halt_lister) + if response.result == StorageResolve.StorageResolveResponse.Result.STORAGE: + if track is None: + pass + elif response.result == StorageResolve.StorageResolveResponse.Result.RESTRICTED: + raise RuntimeError("Content is restricted!") + elif response.result == StorageResolve.StorageResolveResponse.Response.UNRECOGNIZED: + raise RuntimeError("Content is unrecognized!") + else: + raise RuntimeError("Unknown result: {}".format(response.result)) + + def load_episode(self, episode_id: EpisodeId, + audio_quality_picker: AudioQualityPicker, preload: bool, + halt_listener: HaltListener) -> LoadedStream: + episode = self.__session.api().get_metadata_4_episode(episode_id) + if episode.external_url: + return CdnFeedHelper.load_episode_external(self.__session, episode, + halt_listener) + file = audio_quality_picker.get_file(episode.audio) + if file is None: + self.logger.fatal( + "Couldn't find any suitable audio file, available: {}".format( + episode.audio)) + return self.load_stream(file, None, episode, preload, halt_listener) + + def load_track(self, track_id_or_track: typing.Union[TrackId, + Metadata.Track], + audio_quality_picker: AudioQualityPicker, preload: bool, + halt_listener: HaltListener): + if type(track_id_or_track) is TrackId: + original = self.__session.api().get_metadata_4_track( + track_id_or_track) + track = self.pick_alternative_if_necessary(original) + if track is None: + raise RuntimeError("Cannot get alternative track") + else: + track = track_id_or_track + file = audio_quality_picker.get_file(track.file) + if file is None: + self.logger.fatal( + "Couldn't find any suitable audio file, available: {}".format( + track.file)) + raise FeederException() + return self.load_stream(file, track, None, preload, halt_listener) + + def pick_alternative_if_necessary( + self, track: Metadata.Track) -> typing.Union[Metadata.Track, None]: + if len(track.file) > 0: + return track + for alt in track.alternative: + if len(alt.file) > 0: + return Metadata.Track( + gid=track.gid, + name=track.name, + album=track.album, + artist=track.artist, + number=track.number, + disc_number=track.disc_number, + duration=track.duration, + popularity=track.popularity, + explicit=track.explicit, + external_id=track.external_id, + restriction=track.restriction, + file=alt.file, + sale_period=track.sale_period, + preview=track.preview, + tags=track.tags, + earliest_live_timestamp=track.earliest_live_timestamp, + has_lyrics=track.has_lyrics, + availability=track.availability, + licensor=track.licensor) + return None + + def resolve_storage_interactive( + self, file_id: bytes, + preload: bool) -> StorageResolve.StorageResolveResponse: + resp = self.__session.api().send( + "GET", + (self.storage_resolve_interactive_prefetch + if preload else self.storage_resolve_interactive).format( + util.bytes_to_hex(file_id)), + None, + None, + ) + if resp.status_code != 200: + raise RuntimeError(resp.status_code) + body = resp.content + if body is None: + raise RuntimeError("Response body is empty!") + storage_resolve_response = StorageResolve.StorageResolveResponse() + storage_resolve_response.ParseFromString(body) + return storage_resolve_response + + class LoadedStream: + episode: Metadata.Episode + track: Metadata.Track + input_stream: GeneralAudioStream + normalization_data: NormalizationData + metrics: PlayableContentFeeder.Metrics + + def __init__(self, track_or_episode: typing.Union[Metadata.Track, + Metadata.Episode], + input_stream: GeneralAudioStream, + normalization_data: typing.Union[NormalizationData, None], + metrics: PlayableContentFeeder.Metrics): + if type(track_or_episode) is Metadata.Track: + self.track = track_or_episode + self.episode = None + elif type(track_or_episode) is Metadata.Episode: + self.track = None + self.episode = track_or_episode + else: + raise TypeError() + self.input_stream = input_stream + self.normalization_data = normalization_data + self.metrics = metrics + + class Metrics: + file_id: str + preloaded_audio_key: bool + audio_key_time: int + + def __init__(self, file_id: typing.Union[bytes, None], + preloaded_audio_key: bool, audio_key_time: int): + self.file_id = None if file_id is None else util.bytes_to_hex( + file_id) + self.preloaded_audio_key = preloaded_audio_key + self.audio_key_time = audio_key_time + if preloaded_audio_key and audio_key_time != -1: + raise RuntimeError() + + +class StreamId: + file_id: bytes + episode_gid: bytes + + def __init__(self, + file: Metadata.AudioFile = None, + episode: Metadata.Episode = None): + if file is None and episode is None: + return + self.file_id = None if file is None else file.file_id + self.episode_gid = None if episode is None else episode.gid + + def get_file_id(self): + if self.file_id is None: + raise RuntimeError("Not a file!") + return util.bytes_to_hex(self.file_id) + + def is_episode(self): + return self.episode_gid is not None + + def get_episode_gid(self): + if self.episode_gid is None: + raise RuntimeError("Not an episode!") + return util.bytes_to_hex(self.episode_gid) diff --git a/resources/lib/librespot/audio/decoders.py b/resources/lib/librespot/audio/decoders.py new file mode 100644 index 0000000..ddb726c --- /dev/null +++ b/resources/lib/librespot/audio/decoders.py @@ -0,0 +1,81 @@ +from __future__ import annotations +from librespot.audio import SuperAudioFormat +from librespot.proto import Metadata_pb2 as Metadata +from librespot.proto.Metadata_pb2 import AudioFile +from librespot.structure import AudioQualityPicker +import enum +import logging +import typing + + +class AudioQuality(enum.Enum): + NORMAL = 0x00 + HIGH = 0x01 + VERY_HIGH = 0x02 + + @staticmethod + def get_quality(audio_format: AudioFile.Format) -> AudioQuality: + if audio_format in [ + AudioFile.MP3_96, + AudioFile.OGG_VORBIS_96, + AudioFile.AAC_24_NORM, + ]: + return AudioQuality.NORMAL + if audio_format in [ + AudioFile.MP3_160, + AudioFile.MP3_160_ENC, + AudioFile.OGG_VORBIS_160, + AudioFile.AAC_24, + ]: + return AudioQuality.HIGH + if audio_format in [ + AudioFile.MP3_320, + AudioFile.MP3_256, + AudioFile.OGG_VORBIS_320, + AudioFile.AAC_48, + ]: + return AudioQuality.VERY_HIGH + raise RuntimeError("Unknown format: {}".format(format)) + + def get_matches(self, + files: typing.List[AudioFile]) -> typing.List[AudioFile]: + file_list = [] + for file in files: + if hasattr(file, "format") and AudioQuality.get_quality( + file.format) == self: + file_list.append(file) + return file_list + + +class VorbisOnlyAudioQuality(AudioQualityPicker): + logger = logging.getLogger("Librespot:Player:VorbisOnlyAudioQuality") + preferred: AudioQuality + + def __init__(self, preferred: AudioQuality): + self.preferred = preferred + + @staticmethod + def get_vorbis_file(files: typing.List[Metadata.AudioFile]): + for file in files: + if file.HasField("format") and SuperAudioFormat.get( + file.format) == SuperAudioFormat.VORBIS: + return file + return None + + def get_file(self, files: typing.List[Metadata.AudioFile]): + matches: typing.List[Metadata.AudioFile] = self.preferred.get_matches( + files) + vorbis: Metadata.AudioFile = VorbisOnlyAudioQuality.get_vorbis_file( + matches) + if vorbis is None: + vorbis: Metadata.AudioFile = VorbisOnlyAudioQuality.get_vorbis_file( + files) + if vorbis is not None: + self.logger.warning( + "Using {} because preferred {} couldn't be found.".format( + Metadata.AudioFile.Format.Name(vorbis.format), + self.preferred)) + else: + self.logger.fatal( + "Couldn't find any Vorbis file, available: {}") + return vorbis diff --git a/resources/lib/librespot/audio/decrypt.py b/resources/lib/librespot/audio/decrypt.py new file mode 100644 index 0000000..e72cd37 --- /dev/null +++ b/resources/lib/librespot/audio/decrypt.py @@ -0,0 +1,45 @@ +from __future__ import annotations +from Cryptodome.Cipher import AES +from Cryptodome.Util import Counter +from librespot.audio.storage import ChannelManager +from librespot.structure import AudioDecrypt +import io +import time + + +class AesAudioDecrypt(AudioDecrypt): + audio_aes_iv = b'r\xe0g\xfb\xdd\xcb\xcfw\xeb\xe8\xbcd?c\r\x93' + cipher = None + decrypt_count = 0 + decrypt_total_time = 0 + iv_int = int.from_bytes(audio_aes_iv, "big") + iv_diff = 0x100 + key: bytes + + def __init__(self, key: bytes): + self.key = key + + def decrypt_chunk(self, chunk_index: int, buffer: bytes): + new_buffer = io.BytesIO() + iv = self.iv_int + int(ChannelManager.chunk_size * chunk_index / 16) + start = time.time_ns() + for i in range(0, len(buffer), 4096): + cipher = AES.new(key=self.key, + mode=AES.MODE_CTR, + counter=Counter.new(128, initial_value=iv)) + count = min(4096, len(buffer) - i) + decrypted_buffer = cipher.decrypt(buffer[i:i + count]) + new_buffer.write(decrypted_buffer) + if count != len(decrypted_buffer): + raise RuntimeError( + "Couldn't process all data, actual: {}, expected: {}". + format(len(decrypted_buffer), count)) + iv += self.iv_diff + self.decrypt_total_time += time.time_ns() - start + self.decrypt_count += 1 + new_buffer.seek(0) + return new_buffer.read() + + def decrypt_time_ms(self): + return 0 if self.decrypt_count == 0 else int( + (self.decrypt_total_time / self.decrypt_count) / 1000000) diff --git a/resources/lib/librespot/audio/format.py b/resources/lib/librespot/audio/format.py new file mode 100644 index 0000000..24cb6d9 --- /dev/null +++ b/resources/lib/librespot/audio/format.py @@ -0,0 +1,32 @@ +from librespot.proto import Metadata_pb2 as Metadata +import enum + + +class SuperAudioFormat(enum.Enum): + MP3 = 0x00 + VORBIS = 0x01 + AAC = 0x02 + + @staticmethod + def get(audio_format: Metadata.AudioFile.Format): + if audio_format in [ + Metadata.AudioFile.Format.OGG_VORBIS_96, + Metadata.AudioFile.Format.OGG_VORBIS_160, + Metadata.AudioFile.Format.OGG_VORBIS_320, + ]: + return SuperAudioFormat.VORBIS + if audio_format in [ + Metadata.AudioFile.Format.MP3_256, + Metadata.AudioFile.Format.MP3_320, + Metadata.AudioFile.Format.MP3_160, + Metadata.AudioFile.Format.MP3_96, + Metadata.AudioFile.Format.MP3_160_ENC, + ]: + return SuperAudioFormat.MP3 + if audio_format in [ + Metadata.AudioFile.Format.AAC_24, + Metadata.AudioFile.Format.AAC_48, + Metadata.AudioFile.Format.AAC_24_NORM, + ]: + return SuperAudioFormat.AAC + raise RuntimeError("Unknown audio format: {}".format(audio_format)) diff --git a/resources/lib/librespot/audio/storage.py b/resources/lib/librespot/audio/storage.py new file mode 100644 index 0000000..51321e6 --- /dev/null +++ b/resources/lib/librespot/audio/storage.py @@ -0,0 +1,139 @@ +from __future__ import annotations +from librespot import util +from librespot.crypto import Packet +from librespot.proto.Metadata_pb2 import AudioFile +from librespot.structure import Closeable, PacketsReceiver +import concurrent.futures +import io +import logging +import queue +import struct +import threading +import typing + +if typing.TYPE_CHECKING: + from librespot.core import Session + + +class ChannelManager(Closeable, PacketsReceiver): + channels: typing.Dict[int, Channel] = {} + chunk_size = 128 * 1024 + executor_service = concurrent.futures.ThreadPoolExecutor() + logger = logging.getLogger("Librespot:ChannelManager") + seq_holder = 0 + seq_holder_lock = threading.Condition() + __session: Session = None + + def __init__(self, session: Session): + self.__session = session + + def request_chunk(self, file_id: bytes, index: int, file: AudioFile): + start = int(index * self.chunk_size / 4) + end = int((index + 1) * self.chunk_size / 4) + channel = ChannelManager.Channel(self, file, index) + self.channels[channel.chunk_id] = channel + out = io.BytesIO() + out.write(struct.pack(">H", channel.chunk_id)) + out.write(struct.pack(">i", 0x00000000)) + out.write(struct.pack(">i", 0x00000000)) + out.write(struct.pack(">i", 0x00004E20)) + out.write(struct.pack(">i", 0x00030D40)) + out.write(file_id) + out.write(struct.pack(">i", start)) + out.write(struct.pack(">i", end)) + out.seek(0) + self.__session.send(Packet.Type.stream_chunk, out.read()) + + def dispatch(self, packet: Packet) -> None: + payload = io.BytesIO(packet.payload) + if packet.is_cmd(Packet.Type.stream_chunk_res): + chunk_id = struct.unpack(">H", payload.read(2))[0] + channel = self.channels.get(chunk_id) + if channel is None: + self.logger.warning( + "Couldn't find channel, id: {}, received: {}".format( + chunk_id, len(packet.payload))) + return + channel.add_to_queue(payload) + elif packet.is_cmd(Packet.Type.channel_error): + chunk_id = struct.unpack(">H", payload.read(2))[0] + channel = self.channels.get(chunk_id) + if channel is None: + self.logger.warning( + "Dropping channel error, id: {}, code: {}".format( + chunk_id, + struct.unpack(">H", payload.read(2))[0])) + return + channel.stream_error(struct.unpack(">H", payload.read(2))[0]) + else: + self.logger.warning( + "Couldn't handle packet, cmd: {}, payload: {}".format( + packet.cmd, util.bytes_to_hex(packet.payload))) + + def close(self) -> None: + self.executor_service.shutdown() + + class Channel: + channel_manager: ChannelManager + chunk_id: int + q = queue.Queue() + __buffer: io.BytesIO + __chunk_index: int + __file: AudioFile + __header: bool = True + + def __init__(self, channel_manager: ChannelManager, file: AudioFile, + chunk_index: int): + self.__buffer = io.BytesIO() + self.channel_manager = channel_manager + self.__file = file + self.__chunk_index = chunk_index + with self.channel_manager.seq_holder_lock: + self.chunk_id = self.channel_manager.seq_holder + self.channel_manager.seq_holder += 1 + self.channel_manager.executor_service.submit( + lambda: ChannelManager.Channel.Handler(self)) + + def _handle(self, payload: bytes) -> bool: + if len(payload) == 0: + if not self.__header: + self.__file.write_chunk(payload, self.__chunk_index, False) + return True + self.channel_manager.logger.debug( + "Received empty chunk, skipping.") + return False + if self.__header: + length: int + while len(payload.buffer) > 0: + length = payload.read_short() + if not length > 0: + break + header_id = payload.read_byte() + header_data = payload.read(length - 1) + self.__file.write_header(int.from_bytes(header_id, "big"), + bytearray(header_data), False) + self.__header = False + else: + self.__buffer.write(payload.read(len(payload.buffer))) + return False + + def add_to_queue(self, payload): + self.q.put(payload) + + def stream_error(self, code: int) -> None: + self.__file.stream_error(self.__chunk_index, code) + + class Handler: + __channel: ChannelManager.Channel = None + + def __init__(self, channel: ChannelManager.Channel): + self.__channel = channel + + def run(self) -> None: + self.__channel.channel_manager.logger.debug( + "ChannelManager.Handler is starting") + with self.__channel.q.all_tasks_done: + self.__channel.channel_manager.channels.pop( + self.__channel.chunk_id) + self.__channel.channel_manager.logger.debug( + "ChannelManager.Handler is shutting down") diff --git a/resources/lib/librespot/cache.py b/resources/lib/librespot/cache.py new file mode 100644 index 0000000..7937c46 --- /dev/null +++ b/resources/lib/librespot/cache.py @@ -0,0 +1,18 @@ +from __future__ import annotations +import typing + +if typing.TYPE_CHECKING: + from librespot.core import Session + + +class CacheManager: + clean_up_threshold = 604800000 + header_hash = 253 + header_timestamp = 254 + parent: str + + def __init__(self, session: Session): + """ + @Todo Implement function + :param session: + """ diff --git a/resources/lib/librespot/core.py b/resources/lib/librespot/core.py new file mode 100644 index 0000000..f7d602d --- /dev/null +++ b/resources/lib/librespot/core.py @@ -0,0 +1,2307 @@ +from __future__ import annotations + +import base64 +import binascii +import concurrent.futures +import enum +import gzip +import io +import json +import logging +import os +import random +import sched +import socket +import struct +import threading +import time +import typing +import urllib.parse + +import defusedxml.ElementTree +import requests +import websocket +from Cryptodome import Random +from Cryptodome.Cipher import AES +from Cryptodome.Hash import HMAC +from Cryptodome.Hash import SHA1 +from Cryptodome.Protocol.KDF import PBKDF2 +from Cryptodome.PublicKey import RSA +from Cryptodome.Signature import PKCS1_v1_5 + +from librespot import util +from librespot import Version +from librespot.audio import AudioKeyManager +from librespot.audio import CdnManager +from librespot.audio import PlayableContentFeeder +from librespot.audio.storage import ChannelManager +from librespot.cache import CacheManager +from librespot.crypto import CipherPair +from librespot.crypto import DiffieHellman +from librespot.crypto import Packet +from librespot.mercury import MercuryClient +from librespot.mercury import MercuryRequests +from librespot.mercury import RawMercuryRequest +from librespot.metadata import AlbumId +from librespot.metadata import ArtistId +from librespot.metadata import EpisodeId +from librespot.metadata import PlaylistId +from librespot.metadata import ShowId +from librespot.metadata import TrackId +from librespot.proto import Authentication_pb2 as Authentication +from librespot.proto import ClientToken_pb2 as ClientToken +from librespot.proto import Connect_pb2 as Connect +from librespot.proto import Connectivity_pb2 as Connectivity +from librespot.proto import Keyexchange_pb2 as Keyexchange +from librespot.proto import Metadata_pb2 as Metadata +from librespot.proto import Playlist4External_pb2 as Playlist4External +from librespot.proto.ExplicitContentPubsub_pb2 import UserAttributesUpdate +from librespot.structure import Closeable +from librespot.structure import MessageListener +from librespot.structure import RequestListener +from librespot.structure import SubListener + + +class ApiClient(Closeable): + """ """ + logger = logging.getLogger("Librespot:ApiClient") + __base_url: str + __client_token_str: str = None + __session: Session + + def __init__(self, session: Session): + self.__session = session + self.__base_url = "https://{}".format(ApResolver.get_random_spclient()) + + def build_request( + self, + method: str, + suffix: str, + headers: typing.Union[None, typing.Dict[str, str]], + body: typing.Union[None, bytes], + ) -> requests.PreparedRequest: + """ + + :param method: str: + :param suffix: str: + :param headers: typing.Union[None: + :param typing.Dict[str: + :param str]]: + :param body: typing.Union[None: + :param bytes]: + + """ + 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 + request.headers = {} + if headers is not None: + request.headers = headers + request.headers["Authorization"] = "Bearer {}".format( + self.__session.tokens().get("playlist-read")) + request.headers["client-token"] = self.__client_token_str + request.url = self.__base_url + suffix + return request + + def send( + self, + method: str, + suffix: str, + headers: typing.Union[None, typing.Dict[str, str]], + body: typing.Union[None, bytes], + ) -> requests.Response: + """ + + :param method: str: + :param suffix: str: + :param headers: typing.Union[None: + :param typing.Dict[str: + :param str]]: + :param body: typing.Union[None: + :param bytes]: + + """ + response = self.__session.client().send( + self.build_request(method, suffix, headers, body)) + return response + + def put_connect_state(self, connection_id: str, + proto: Connect.PutStateRequest) -> None: + """ + + :param connection_id: str: + :param proto: Connect.PutStateRequest: + + """ + response = self.send( + "PUT", + "/connect-state/v1/devices/{}".format(self.__session.device_id()), + { + "Content-Type": "application/protobuf", + "X-Spotify-Connection-Id": connection_id, + }, + proto.SerializeToString(), + ) + if response.status_code == 413: + self.logger.warning( + "PUT state payload is too large: {} bytes uncompressed.". + format(len(proto.SerializeToString()))) + elif response.status_code != 200: + self.logger.warning("PUT state returned {}. headers: {}".format( + response.status_code, response.headers)) + + def get_metadata_4_track(self, track: TrackId) -> Metadata.Track: + """ + + :param track: TrackId: + + """ + response = self.send("GET", + "/metadata/4/track/{}".format(track.hex_id()), + None, None) + ApiClient.StatusCodeException.check_status(response) + body = response.content + if body is None: + raise RuntimeError() + proto = Metadata.Track() + proto.ParseFromString(body) + return proto + + def get_metadata_4_episode(self, episode: EpisodeId) -> Metadata.Episode: + """ + + :param episode: EpisodeId: + + """ + response = self.send("GET", + "/metadata/4/episode/{}".format(episode.hex_id()), + None, None) + ApiClient.StatusCodeException.check_status(response) + body = response.content + if body is None: + raise IOError() + proto = Metadata.Episode() + proto.ParseFromString(body) + return proto + + def get_metadata_4_album(self, album: AlbumId) -> Metadata.Album: + """ + + :param album: AlbumId: + + """ + response = self.send("GET", + "/metadata/4/album/{}".format(album.hex_id()), + None, None) + ApiClient.StatusCodeException.check_status(response) + + body = response.content + if body is None: + raise IOError() + proto = Metadata.Album() + proto.ParseFromString(body) + return proto + + def get_metadata_4_artist(self, artist: ArtistId) -> Metadata.Artist: + """ + + :param artist: ArtistId: + + """ + response = self.send("GET", + "/metadata/4/artist/{}".format(artist.hex_id()), + None, None) + ApiClient.StatusCodeException.check_status(response) + body = response.content + if body is None: + raise IOError() + proto = Metadata.Artist() + proto.ParseFromString(body) + return proto + + def get_metadata_4_show(self, show: ShowId) -> Metadata.Show: + """ + + :param show: ShowId: + + """ + response = self.send("GET", + "/metadata/4/show/{}".format(show.hex_id()), None, + None) + ApiClient.StatusCodeException.check_status(response) + body = response.content + if body is None: + raise IOError() + proto = Metadata.Show() + proto.ParseFromString(body) + return proto + + def get_playlist(self, + _id: PlaylistId) -> Playlist4External.SelectedListContent: + """ + + :param _id: PlaylistId: + + """ + response = self.send("GET", + "/playlist/v2/playlist/{}".format(_id.id()), None, + None) + ApiClient.StatusCodeException.check_status(response) + body = response.content + if body is None: + raise IOError() + proto = Playlist4External.SelectedListContent() + proto.ParseFromString(body) + return proto + + def set_client_token(self, client_token): + """ + + :param 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 + + def __init__(self, response: requests.Response): + super().__init__(response.status_code) + self.code = response.status_code + + @staticmethod + def check_status(response: requests.Response) -> None: + """ + + :param response: requests.Response: + + """ + if response.status_code != 200: + raise ApiClient.StatusCodeException(response) + + +class ApResolver: + """ """ + base_url = "https://apresolve.spotify.com/" + + @staticmethod + def request(service_type: str) -> typing.Any: + """Gets the specified ApResolve + + :param service_type: str: + :returns: The resulting object will be returned + + """ + response = requests.get("{}?type={}".format(ApResolver.base_url, + service_type)) + if response.status_code != 200: + if response.status_code == 502: + raise RuntimeError( + f"ApResolve request failed with the following return value: {response.content}. Servers might be down!" + ) + return response.json() + + @staticmethod + def get_random_of(service_type: str) -> str: + """Gets the specified random ApResolve url + + :param service_type: str: + :returns: A random ApResolve url will be returned + + """ + pool = ApResolver.request(service_type) + urls = pool.get(service_type) + if urls is None or len(urls) == 0: + raise RuntimeError("No ApResolve url available") + return random.choice(urls) + + @staticmethod + def get_random_dealer() -> str: + """Get dealer endpoint url + + + :returns: dealer endpoint url + + """ + return ApResolver.get_random_of("dealer") + + @staticmethod + def get_random_spclient() -> str: + """Get spclient endpoint url + + + :returns: spclient endpoint url + + """ + return ApResolver.get_random_of("spclient") + + @staticmethod + def get_random_accesspoint() -> str: + """Get accesspoint endpoint url + + + :returns: accesspoint endpoint url + + """ + return ApResolver.get_random_of("accesspoint") + + +class DealerClient(Closeable): + """ """ + logger = logging.getLogger("Librespot:DealerClient") + __connection: typing.Union[ConnectionHolder, None] + __last_scheduled_reconnection: typing.Union[sched.Event, None] + __message_listeners: typing.Dict[MessageListener, typing.List[str]] = {} + __message_listeners_lock = threading.Condition() + __request_listeners: typing.Dict[str, RequestListener] = {} + __request_listeners_lock = threading.Condition() + __scheduler = sched.scheduler() + __session: Session + __worker = concurrent.futures.ThreadPoolExecutor() + + def __init__(self, session: Session): + self.__session = session + + def add_message_listener(self, listener: MessageListener, + uris: list[str]) -> None: + """ + + :param listener: MessageListener: + :param uris: list[str]: + + """ + with self.__message_listeners_lock: + if listener in self.__message_listeners: + raise TypeError( + "A listener for {} has already been added.".format(uris)) + self.__message_listeners[listener] = uris + self.__message_listeners_lock.notify_all() + + def add_request_listener(self, listener: RequestListener, uri: str): + """ + + :param listener: RequestListener: + :param uri: str: + + """ + with self.__request_listeners_lock: + if uri in self.__request_listeners: + raise TypeError( + "A listener for '{}' has already been added.".format(uri)) + self.__request_listeners[uri] = listener + self.__request_listeners_lock.notify_all() + + def close(self) -> None: + """ """ + self.__worker.shutdown() + + def connect(self) -> None: + """ """ + self.__connection = DealerClient.ConnectionHolder( + self.__session, + self, + "wss://{}/?access_token={}".format( + ApResolver.get_random_dealer(), + self.__session.tokens().get("playlist-read"), + ), + ) + + def connection_invalided(self) -> None: + """ """ + self.__connection = None + self.logger.debug("Scheduled reconnection attempt in 10 seconds...") + + def anonymous(): + """ """ + self.__last_scheduled_reconnection = None + self.connect() + + self.__last_scheduled_reconnection = self.__scheduler.enter( + 10, 1, anonymous) + + def handle_message(self, obj: typing.Any) -> None: + """ + + :param obj: typing.Any: + + """ + uri = obj.get("uri") + headers = self.__get_headers(obj) + payloads = obj.get("payloads") + decoded_payloads: typing.Any + if payloads is not None: + if headers.get("Content-Type") == "application/json": + decoded_payloads = payloads + elif headers.get("Content-Type") == "plain/text": + decoded_payloads = payloads + else: + decoded_payloads = base64.b64decode(payloads) + if headers.get("Transfer-Encoding") == "gzip": + decoded_payloads = gzip.decompress(decoded_payloads) + else: + decoded_payloads = b"" + interesting = False + with self.__message_listeners_lock: + for listener in self.__message_listeners: + dispatched = False + keys = self.__message_listeners.get(listener) + for key in keys: + if uri.startswith(key) and not dispatched: + interesting = True + + def anonymous(): + """ """ + listener.on_message(uri, headers, decoded_payloads) + + self.__worker.submit(anonymous) + dispatched = True + if not interesting: + self.logger.debug("Couldn't dispatch message: {}".format(uri)) + + def handle_request(self, obj: typing.Any) -> None: + """ + + :param obj: typing.Any: + + """ + mid = obj.get("message_ident") + key = obj.get("key") + headers = self.__get_headers(obj) + payload = obj.get("payload") + if headers.get("Transfer-Encoding") == "gzip": + gz = base64.b64decode(payload.get("compressed")) + payload = json.loads(gzip.decompress(gz)) + pid = payload.get("message_id") + sender = payload.get("sent_by_device_id") + command = payload.get("command") + self.logger.debug( + "Received request. [mid: {}, key: {}, pid: {}, sender: {}, command: {}]" + .format(mid, key, pid, sender, command)) + interesting = False + with self.__request_listeners_lock: + for mid_prefix in self.__request_listeners: + if mid.startswith(mid_prefix): + listener = self.__request_listeners.get(mid_prefix) + interesting = True + + def anonymous(): + """ """ + result = listener.on_request(mid, pid, sender, command) + if self.__connection is not None: + self.__connection.send_reply(key, result) + self.logger.warning( + "Handled request. [key: {}, result: {}]".format( + key, result)) + + self.__worker.submit(anonymous) + if not interesting: + self.logger.debug("Couldn't dispatch request: {}".format(mid)) + + def remove_message_listener(self, listener: MessageListener) -> None: + """ + + :param listener: MessageListener: + + """ + with self.__message_listeners_lock: + self.__message_listeners.pop(listener) + + def remove_request_listener(self, listener: RequestListener) -> None: + """ + + :param listener: RequestListener: + + """ + with self.__request_listeners_lock: + request_listeners = {} + for key, value in self.__request_listeners.items(): + if value != listener: + request_listeners[key] = value + self.__request_listeners = request_listeners + + def wait_for_listener(self) -> None: + """ """ + with self.__message_listeners_lock: + if self.__message_listeners == {}: + return + self.__message_listeners_lock.wait() + + def __get_headers(self, obj: typing.Any) -> dict[str, str]: + headers = obj.get("headers") + if headers is None: + return {} + return headers + + class ConnectionHolder(Closeable): + """ """ + __closed = False + __dealer_client: DealerClient + __last_scheduled_ping: sched.Event + __received_pong = False + __scheduler = sched.scheduler() + __session: Session + __url: str + __ws: websocket.WebSocketApp + + def __init__(self, session: Session, dealer_client: DealerClient, + url: str): + self.__session = session + self.__dealer_client = dealer_client + self.__url = url + self.__ws = websocket.WebSocketApp(url) + + def close(self): + """ """ + if not self.__closed: + self.__ws.close() + self.__closed = True + if self.__last_scheduled_ping is not None: + self.__scheduler.cancel(self.__last_scheduled_ping) + + def on_failure(self, ws: websocket.WebSocketApp, error): + """ + + :param ws: websocket.WebSocketApp: + :param error: + + """ + if self.__closed: + return + self.__dealer_client.logger.warning( + "An exception occurred. Reconnecting...") + self.close() + + def on_message(self, ws: websocket.WebSocketApp, text: str): + """ + + :param ws: websocket.WebSocketApp: + :param text: str: + + """ + obj = json.loads(text) + self.__dealer_client.wait_for_listener() + typ = MessageType.parse(obj.get("type")) + if typ == MessageType.MESSAGE: + self.__dealer_client.handle_message(obj) + elif typ == MessageType.REQUEST: + self.__dealer_client.handle_request(obj) + elif typ == MessageType.PONG: + self.__received_pong = True + elif typ == MessageType.PING: + pass + else: + raise RuntimeError("Unknown message type for {}".format( + typ.value)) + + def on_open(self, ws: websocket.WebSocketApp): + """ + + :param ws: websocket.WebSocketApp: + + """ + if self.__closed: + self.__dealer_client.logger.fatal( + "I wonder what happened here... Terminating. [closed: {}]". + format(self.__closed)) + self.__dealer_client.logger.debug( + "Dealer connected! [url: {}]".format(self.__url)) + + def anonymous(): + """ """ + self.send_ping() + self.__received_pong = False + + def anonymous2(): + """ """ + if self.__last_scheduled_ping is None: + return + if not self.__received_pong: + self.__dealer_client.logger.warning( + "Did not receive ping in 3 seconds. Reconnecting..." + ) + self.close() + return + self.__received_pong = False + + self.__scheduler.enter(3, 1, anonymous2) + self.__last_scheduled_ping = self.__scheduler.enter( + 30, 1, anonymous) + + self.__last_scheduled_ping = self.__scheduler.enter( + 30, 1, anonymous) + + def send_ping(self): + """ """ + self.__ws.send('{"type":"ping"}') + + def send_reply(self, key: str, result: DealerClient.RequestResult): + """ + + :param key: str: + :param result: DealerClient.RequestResult: + + """ + success = ("true" if result == DealerClient.RequestResult.SUCCESS + else "false") + self.__ws.send( + '{"type":"reply","key":"%s","payload":{"success":%s}' % + (key, success)) + + class RequestResult(enum.Enum): + """ """ + UNKNOWN_SEND_COMMAND_RESULT = 0 + SUCCESS = 1 + DEVICE_NOT_FOUND = 2 + CONTEXT_PLAYER_ERROR = 3 + DEVICE_DISAPPEARED = 4 + UPSTREAM_ERROR = 5 + DEVICE_DOES_NOT_SUPPORT_COMMAND = 6 + RATE_LIMITED = 7 + + +class EventService(Closeable): + """ """ + logger = logging.getLogger("Librespot:EventService") + __session: Session + __worker = concurrent.futures.ThreadPoolExecutor() + + def __init__(self, session: Session): + self.__session = session + + def __worker_callback(self, event_builder: EventBuilder): + try: + body = event_builder.to_array() + resp = self.__session.mercury().send_sync( + RawMercuryRequest.Builder().set_uri( + "hm://event-service/v1/events").set_method("POST"). + add_user_field("Accept-Language", "en").add_user_field( + "X-ClientTimeStamp", + int(time.time() * 1000)).add_payload_part(body).build()) + self.logger.debug("Event sent. body: {}, result: {}".format( + body, resp.status_code)) + except IOError as ex: + self.logger.error("Failed sending event: {} {}".format( + event_builder, ex)) + + def send_event(self, event_or_builder: typing.Union[GenericEvent, + EventBuilder]): + """ + + :param event_or_builder: typing.Union[GenericEvent: + :param EventBuilder]: + + """ + if type(event_or_builder) is EventService.GenericEvent: + builder = event_or_builder.build() + elif type(event_or_builder) is EventService.EventBuilder: + builder = event_or_builder + else: + raise TypeError() + self.__worker.submit(lambda: self.__worker_callback(builder)) + + def language(self, lang: str): + """ + + :param lang: str: + + """ + event = EventService.EventBuilder(EventService.Type.LANGUAGE) + event.append(s=lang) + + def close(self): + """ """ + self.__worker.shutdown() + + class Type(enum.Enum): + """ """ + LANGUAGE = ("812", 1) + FETCHED_FILE_ID = ("274", 3) + NEW_SESSION_ID = ("557", 3) + NEW_PLAYBACK_ID = ("558", 1) + TRACK_PLAYED = ("372", 1) + TRACK_TRANSITION = ("12", 37) + CDN_REQUEST = ("10", 20) + + eventId: str + unknown: str + + def __init__(self, event_id: str, unknown: str): + self.eventId = event_id + self.unknown = unknown + + class GenericEvent: + """ """ + + def build(self) -> EventService.EventBuilder: + """ """ + raise NotImplementedError + + class EventBuilder: + """ """ + body: io.BytesIO + + def __init__(self, event_type: EventService.Type): + self.body = io.BytesIO() + self.append_no_delimiter(event_type.value[0]) + self.append(event_type.value[1]) + + def append_no_delimiter(self, s: str = None) -> None: + """ + + :param s: str: (Default value = None) + + """ + if s is None: + s = "" + self.body.write(s.encode()) + + def append(self, + c: int = None, + s: str = None) -> EventService.EventBuilder: + """ + + :param c: int: (Default value = None) + :param s: str: (Default value = None) + + """ + if c is None and s is None or c is not None and s is not None: + raise TypeError() + if c is not None: + self.body.write(b"\x09") + self.body.write(bytes([c])) + return self + if s is not None: + self.body.write(b"\x09") + self.append_no_delimiter(s) + return self + + def to_array(self) -> bytes: + """ """ + pos = self.body.tell() + self.body.seek(0) + data = self.body.read() + self.body.seek(pos) + return data + + +class MessageType(enum.Enum): + """ """ + MESSAGE = "message" + PING = "ping" + PONG = "pong" + REQUEST = "request" + + @staticmethod + def parse(_typ: str): + """ + + :param _typ: str: + + """ + if _typ == MessageType.MESSAGE.value: + return MessageType.MESSAGE + if _typ == MessageType.PING.value: + return MessageType.PING + if _typ == MessageType.PONG.value: + return MessageType.PONG + if _typ == MessageType.REQUEST.value: + return MessageType.REQUEST + raise TypeError("Unknown MessageType: {}".format(_typ)) + + +class Session(Closeable, MessageListener, SubListener): + """ """ + cipher_pair: typing.Union[CipherPair, None] + country_code: str = "EN" + connection: typing.Union[ConnectionHolder, None] + logger = logging.getLogger("Librespot:Session") + scheduled_reconnect: typing.Union[sched.Event, None] = None + scheduler = sched.scheduler(time.time) + __api: ApiClient + __ap_welcome: Authentication.APWelcome + __audio_key_manager: typing.Union[AudioKeyManager, None] = None + __auth_lock = threading.Condition() + __auth_lock_bool = False + __cache_manager: typing.Union[CacheManager, None] + __cdn_manager: typing.Union[CdnManager, None] + __channel_manager: typing.Union[ChannelManager, None] = None + __client: typing.Union[requests.Session, None] + __closed = False + __closing = False + __content_feeder: typing.Union[PlayableContentFeeder, None] + __dealer_client: typing.Union[DealerClient, None] = None + __event_service: typing.Union[EventService, None] = None + __keys: DiffieHellman + __mercury_client: MercuryClient + __receiver: typing.Union[Receiver, None] = None + __search: typing.Union[SearchManager, None] + __server_key = (b"\xac\xe0F\x0b\xff\xc20\xaf\xf4k\xfe\xc3\xbf\xbf\x86=" + b"\xa1\x91\xc6\xcc3l\x93\xa1O\xb3\xb0\x16\x12\xac\xacj" + b"\xf1\x80\xe7\xf6\x14\xd9B\x9d\xbe.4fC\xe3b\xd22z\x1a" + b"\r\x92;\xae\xdd\x14\x02\xb1\x81U\x05a\x04\xd5,\x96\xa4" + b"L\x1e\xcc\x02J\xd4\xb2\x0c\x00\x1f\x17\xed\xc2/\xc45" + b"!\xc8\xf0\xcb\xae\xd2\xad\xd7+\x0f\x9d\xb3\xc52\x1a*" + b"\xfeY\xf3Z\r\xach\xf1\xfab\x1e\xfb,\x8d\x0c\xb79-\x92" + b"G\xe3\xd75\x1am\xbd$\xc2\xae%[\x88\xff\xabs)\x8a\x0b" + b"\xcc\xcd\x0cXg1\x89\xe8\xbd4\x80xJ_\xc9k\x89\x9d\x95k" + b"\xfc\x86\xd7O3\xa6x\x17\x96\xc9\xc3-\r2\xa5\xab\xcd\x05'" + b"\xe2\xf7\x10\xa3\x96\x13\xc4/\x99\xc0'\xbf\xed\x04\x9c" + b"<'X\x04\xb6\xb2\x19\xf9\xc1/\x02\xe9Hc\xec\xa1\xb6B\xa0" + b"\x9dH%\xf8\xb3\x9d\xd0\xe8j\xf9HM\xa1\xc2\xba\x860B\xea" + b"\x9d\xb3\x08l\x19\x0eH\xb3\x9df\xeb\x00\x06\xa2Z\xee\xa1" + b"\x1b\x13\x87<\xd7\x19\xe6U\xbd") + __stored_str: str = "" + __token_provider: typing.Union[TokenProvider, None] + __user_attributes = {} + + def __init__(self, inner: Inner, address: str) -> None: + self.__client = Session.create_client(inner.conf) + self.connection = Session.ConnectionHolder.create(address, None) + self.__inner = inner + self.__keys = DiffieHellman() + self.logger.info("Created new session! device_id: {}, ap: {}".format( + inner.device_id, address)) + + def api(self) -> ApiClient: + """ """ + self.__wait_auth_lock() + if self.__api is None: + raise RuntimeError("Session isn't authenticated!") + return self.__api + + def ap_welcome(self): + """ """ + self.__wait_auth_lock() + if self.__ap_welcome is None: + raise RuntimeError("Session isn't authenticated!") + return self.__ap_welcome + + def audio_key(self) -> AudioKeyManager: + """ """ + self.__wait_auth_lock() + if self.__audio_key_manager is None: + raise RuntimeError("Session isn't authenticated!") + return self.__audio_key_manager + + def authenticate(self, + credential: Authentication.LoginCredentials) -> None: + """Log in to Spotify + + :param credential: Spotify account login information + :param credential: Authentication.LoginCredentials: + + """ + self.__authenticate_partial(credential, False) + with self.__auth_lock: + self.__mercury_client = MercuryClient(self) + self.__token_provider = TokenProvider(self) + self.__audio_key_manager = AudioKeyManager(self) + self.__channel_manager = ChannelManager(self) + self.__api = ApiClient(self) + self.__cdn_manager = CdnManager(self) + self.__content_feeder = PlayableContentFeeder(self) + self.__cache_manager = CacheManager(self) + self.__dealer_client = DealerClient(self) + self.__search = SearchManager(self) + self.__event_service = EventService(self) + self.__auth_lock_bool = False + self.__auth_lock.notify_all() + self.dealer().connect() + self.logger.info("Authenticated as {}!".format( + self.__ap_welcome.canonical_username)) + self.mercury().interested_in("spotify:user:attributes:update", self) + self.dealer().add_message_listener( + self, ["hm://connect-state/v1/connect/logout"]) + + def cache(self) -> CacheManager: + """ """ + self.__wait_auth_lock() + if self.__cache_manager is None: + raise RuntimeError("Session isn't authenticated!") + return self.__cache_manager + + def cdn(self) -> CdnManager: + """ """ + self.__wait_auth_lock() + if self.__cdn_manager is None: + raise RuntimeError("Session isn't authenticated!") + return self.__cdn_manager + + def channel(self) -> ChannelManager: + """ """ + self.__wait_auth_lock() + if self.__channel_manager is None: + raise RuntimeError("Session isn't authenticated!") + return self.__channel_manager + + def client(self) -> requests.Session: + """ """ + return self.__client + + def close(self) -> None: + """Close instance""" + self.logger.info("Closing session. device_id: {}".format( + self.__inner.device_id)) + self.__closing = True + if self.__dealer_client is not None: + self.__dealer_client.close() + self.__dealer_client = None + if self.__audio_key_manager is not None: + self.__audio_key_manager = None + if self.__channel_manager is not None: + self.__channel_manager.close() + self.__channel_manager = None + if self.__event_service is not None: + self.__event_service.close() + self.__event_service = None + if self.__receiver is not None: + self.__receiver.stop() + self.__receiver = None + if self.__client is not None: + self.__client.close() + self.__client = None + if self.connection is not None: + self.connection.close() + self.connection = None + with self.__auth_lock: + self.__ap_welcome = None + self.cipher_pair = None + self.__closed = True + self.logger.info("Closed session. device_id: {}".format( + self.__inner.device_id)) + + def connect(self) -> None: + """Connect to the Spotify Server""" + acc = Session.Accumulator() + # Send ClientHello + nonce = Random.get_random_bytes(0x10) + client_hello_proto = Keyexchange.ClientHello( + build_info=Version.standard_build_info(), + client_nonce=nonce, + cryptosuites_supported=[ + Keyexchange.Cryptosuite.CRYPTO_SUITE_SHANNON + ], + login_crypto_hello=Keyexchange.LoginCryptoHelloUnion( + diffie_hellman=Keyexchange.LoginCryptoDiffieHellmanHello( + gc=self.__keys.public_key_bytes(), server_keys_known=1), ), + padding=b"\x1e", + ) + client_hello_bytes = client_hello_proto.SerializeToString() + self.connection.write(b"\x00\x04") + self.connection.write_int(2 + 4 + len(client_hello_bytes)) + self.connection.write(client_hello_bytes) + self.connection.flush() + acc.write(b"\x00\x04") + acc.write_int(2 + 4 + len(client_hello_bytes)) + acc.write(client_hello_bytes) + # Read APResponseMessage + ap_response_message_length = self.connection.read_int() + acc.write_int(ap_response_message_length) + ap_response_message_bytes = self.connection.read( + ap_response_message_length - 4) + acc.write(ap_response_message_bytes) + ap_response_message_proto = Keyexchange.APResponseMessage() + ap_response_message_proto.ParseFromString(ap_response_message_bytes) + shared_key = util.int_to_bytes( + self.__keys.compute_shared_key( + ap_response_message_proto.challenge.login_crypto_challenge. + diffie_hellman.gs)) + # Check gs_signature + rsa = RSA.construct((int.from_bytes(self.__server_key, "big"), 65537)) + pkcs1_v1_5 = PKCS1_v1_5.new(rsa) + sha1 = SHA1.new() + sha1.update(ap_response_message_proto.challenge.login_crypto_challenge. + diffie_hellman.gs) + if not pkcs1_v1_5.verify( + sha1, + ap_response_message_proto.challenge.login_crypto_challenge. + diffie_hellman.gs_signature, + ): + raise RuntimeError("Failed signature check!") + # Solve challenge + buffer = io.BytesIO() + for i in range(1, 6): + mac = HMAC.new(shared_key, digestmod=SHA1) + mac.update(acc.read()) + mac.update(bytes([i])) + buffer.write(mac.digest()) + buffer.seek(0) + mac = HMAC.new(buffer.read(20), digestmod=SHA1) + mac.update(acc.read()) + challenge = mac.digest() + client_response_plaintext_proto = Keyexchange.ClientResponsePlaintext( + crypto_response=Keyexchange.CryptoResponseUnion(), + login_crypto_response=Keyexchange.LoginCryptoResponseUnion( + diffie_hellman=Keyexchange.LoginCryptoDiffieHellmanResponse( + hmac=challenge)), + pow_response=Keyexchange.PoWResponseUnion(), + ) + client_response_plaintext_bytes = ( + client_response_plaintext_proto.SerializeToString()) + self.connection.write_int(4 + len(client_response_plaintext_bytes)) + self.connection.write(client_response_plaintext_bytes) + self.connection.flush() + try: + self.connection.set_timeout(1) + scrap = self.connection.read(4) + if len(scrap) == 4: + payload = self.connection.read( + struct.unpack(">i", scrap)[0] - 4) + failed = Keyexchange.APResponseMessage() + failed.ParseFromString(payload) + raise RuntimeError(failed) + except socket.timeout: + pass + finally: + self.connection.set_timeout(0) + buffer.seek(20) + with self.__auth_lock: + self.cipher_pair = CipherPair(buffer.read(32), buffer.read(32)) + self.__auth_lock_bool = True + self.logger.info("Connection successfully!") + + def content_feeder(self) -> PlayableContentFeeder: + """ """ + self.__wait_auth_lock() + if self.__content_feeder is None: + raise RuntimeError("Session isn't authenticated!") + return self.__content_feeder + + @staticmethod + def create_client(conf: Configuration) -> requests.Session: + """ + + :param conf: Configuration: + + """ + client = requests.Session() + return client + + def dealer(self) -> DealerClient: + """ """ + self.__wait_auth_lock() + if self.__dealer_client is None: + raise RuntimeError("Session isn't authenticated!") + return self.__dealer_client + + def device_id(self) -> str: + """ """ + return self.__inner.device_id + + def device_name(self) -> str: + """ """ + return self.__inner.device_name + + def device_type(self) -> Connect.DeviceType: + """ """ + return self.__inner.device_type + + def event(self, resp: MercuryClient.Response) -> None: + """ + + :param resp: MercuryClient.Response: + + """ + if resp.uri == "spotify:user:attributes:update": + attributes_update = UserAttributesUpdate() + attributes_update.ParseFromString(resp.payload) + for pair in attributes_update.pairs_list: + self.__user_attributes[pair.key] = pair.value + self.logger.info("Updated user attribute: {} -> {}".format( + pair.key, pair.value)) + + def get_user_attribute(self, key: str, fallback: str = None) -> str: + """ + + :param key: str: + :param fallback: str: (Default value = None) + + """ + return (self.__user_attributes.get(key) + if self.__user_attributes.get(key) is not None else fallback) + + def is_valid(self) -> bool: + """ """ + if self.__closed: + return False + self.__wait_auth_lock() + return self.__ap_welcome is not None and self.connection is not None + + def mercury(self) -> MercuryClient: + """ """ + self.__wait_auth_lock() + if self.__mercury_client is None: + raise RuntimeError("Session isn't authenticated!") + return self.__mercury_client + + def on_message(self, uri: str, headers: typing.Dict[str, str], + payload: bytes): + """ + + :param uri: str: + :param headers: typing.Dict[str: + :param str]: + :param payload: bytes: + + """ + if uri == "hm://connect-state/v1/connect/logout": + self.close() + + def parse_product_info(self, data) -> None: + """Parse product information + + :param data: Raw product information + + """ + products = defusedxml.ElementTree.fromstring(data) + if products is None: + return + product = products[0] + if product is None: + return + for i in range(len(product)): + self.__user_attributes[product[i].tag] = product[i].text + self.logger.debug("Parsed product info: {}".format( + self.__user_attributes)) + + def preferred_locale(self) -> str: + """ """ + return self.__inner.preferred_locale + + def reconnect(self) -> None: + """Reconnect to the Spotify Server""" + if self.connection is not None: + self.connection.close() + self.__receiver.stop() + self.connection = Session.ConnectionHolder.create( + ApResolver.get_random_accesspoint(), self.__inner.conf) + self.connect() + self.__authenticate_partial( + Authentication.LoginCredentials( + typ=self.__ap_welcome.reusable_auth_credentials_type, + username=self.__ap_welcome.canonical_username, + auth_data=self.__ap_welcome.reusable_auth_credentials, + ), + True, + ) + self.logger.info("Re-authenticated as {}!".format( + self.__ap_welcome.canonical_username)) + + def reconnecting(self) -> bool: + """ """ + return not self.__closing and not self.__closed and self.connection is None + + def search(self) -> SearchManager: + """ """ + self.__wait_auth_lock() + if self.__search is None: + raise RuntimeError("Session isn't authenticated!") + return self.__search + + def send(self, cmd: bytes, payload: bytes): + """Send data to socket using send_unchecked + + :param cmd: Command + :param payload: Payload + :param cmd: bytes: + :param payload: bytes: + + """ + if self.__closing and self.connection is None: + self.logger.debug("Connection was broken while closing.") + return + if self.__closed: + raise RuntimeError("Session is closed!") + with self.__auth_lock: + if self.cipher_pair is None or self.__auth_lock_bool: + self.__auth_lock.wait() + self.__send_unchecked(cmd, payload) + + def tokens(self) -> TokenProvider: + """ """ + self.__wait_auth_lock() + if self.__token_provider is None: + raise RuntimeError("Session isn't authenticated!") + return self.__token_provider + + def username(self): + """ """ + return self.__ap_welcome.canonical_username + + def stored(self): + """ """ + return self.__stored_str + + def __authenticate_partial(self, + credential: Authentication.LoginCredentials, + remove_lock: bool) -> None: + """ + Login to Spotify + Args: + credential: Spotify account login information + """ + if self.cipher_pair is None: + raise RuntimeError("Connection not established!") + client_response_encrypted_proto = Authentication.ClientResponseEncrypted( + login_credentials=credential, + system_info=Authentication.SystemInfo( + os=Authentication.Os.OS_UNKNOWN, + cpu_family=Authentication.CpuFamily.CPU_UNKNOWN, + system_information_string=Version.system_info_string(), + device_id=self.__inner.device_id, + ), + version_string=Version.version_string(), + ) + self.__send_unchecked( + Packet.Type.login, + client_response_encrypted_proto.SerializeToString()) + packet = self.cipher_pair.receive_encoded(self.connection) + if packet.is_cmd(Packet.Type.ap_welcome): + self.__ap_welcome = Authentication.APWelcome() + self.__ap_welcome.ParseFromString(packet.payload) + self.__receiver = Session.Receiver(self) + bytes0x0f = Random.get_random_bytes(0x14) + self.__send_unchecked(Packet.Type.unknown_0x0f, bytes0x0f) + preferred_locale = io.BytesIO() + preferred_locale.write(b"\x00\x00\x10\x00\x02preferred-locale" + + self.__inner.preferred_locale.encode()) + preferred_locale.seek(0) + self.__send_unchecked(Packet.Type.preferred_locale, + preferred_locale.read()) + if remove_lock: + with self.__auth_lock: + self.__auth_lock_bool = False + self.__auth_lock.notify_all() + if self.__inner.conf.store_credentials: + reusable = self.__ap_welcome.reusable_auth_credentials + reusable_type = Authentication.AuthenticationType.Name( + self.__ap_welcome.reusable_auth_credentials_type) + if self.__inner.conf.stored_credentials_file is None: + raise TypeError( + "The file path to be saved is not specified") + self.__stored_str = base64.b64encode( + json.dumps({ + "username": + self.__ap_welcome.canonical_username, + "credentials": + base64.b64encode(reusable).decode(), + "type": + reusable_type, + }).encode()).decode() + with open(self.__inner.conf.stored_credentials_file, "w") as f: + json.dump( + { + "username": self.__ap_welcome.canonical_username, + "credentials": base64.b64encode(reusable).decode(), + "type": reusable_type, + }, + f, + ) + + elif packet.is_cmd(Packet.Type.auth_failure): + ap_login_failed = Keyexchange.APLoginFailed() + ap_login_failed.ParseFromString(packet.payload) + self.close() + raise Session.SpotifyAuthenticationException(ap_login_failed) + else: + raise RuntimeError("Unknown CMD 0x" + packet.cmd.hex()) + + def __send_unchecked(self, cmd: bytes, payload: bytes) -> None: + self.cipher_pair.send_encoded(self.connection, cmd, payload) + + def __wait_auth_lock(self) -> None: + if self.__closing and self.connection is None: + self.logger.debug("Connection was broken while closing.") + return + if self.__closed: + raise RuntimeError("Session is closed!") + with self.__auth_lock: + if self.cipher_pair is None or self.__auth_lock_bool: + self.__auth_lock.wait() + + class AbsBuilder: + """ """ + conf = None + device_id = None + device_name = "librespot-python" + device_type = Connect.DeviceType.COMPUTER + preferred_locale = "en" + + def __init__(self, conf: Session.Configuration = None): + if conf is None: + self.conf = Session.Configuration.Builder().build() + else: + self.conf = conf + + def set_preferred_locale(self, locale: str) -> Session.AbsBuilder: + """ + + :param locale: str: + + """ + if len(locale) != 2: + raise TypeError("Invalid locale: {}".format(locale)) + self.preferred_locale = locale + return self + + def set_device_name(self, device_name: str) -> Session.AbsBuilder: + """ + + :param device_name: str: + + """ + self.device_name = device_name + return self + + def set_device_id(self, device_id: str) -> Session.AbsBuilder: + """ + + :param device_id: str: + + """ + if self.device_id is not None and len(device_id) != 40: + raise TypeError("Device ID must be 40 chars long.") + self.device_id = device_id + return self + + def set_device_type( + self, device_type: Connect.DeviceType) -> Session.AbsBuilder: + """ + + :param device_type: Connect.DeviceType: + + """ + self.device_type = device_type + return self + + class Accumulator: + """ """ + __buffer: io.BytesIO + + def __init__(self): + self.__buffer = io.BytesIO() + + def read(self) -> bytes: + """Read all buffer + + + :returns: All buffer + + """ + pos = self.__buffer.tell() + self.__buffer.seek(0) + data = self.__buffer.read() + self.__buffer.seek(pos) + return data + + def write(self, data: bytes) -> None: + """Write data to buffer + + :param data: Bytes to be written + :param data: bytes: + + """ + self.__buffer.write(data) + + def write_int(self, data: int) -> None: + """Write data to buffer + + :param data: Integer to be written + :param data: int: + + """ + self.write(struct.pack(">i", data)) + + def write_short(self, data: int) -> None: + """Write data to buffer + + :param data: Short integer to be written + :param data: int: + + """ + self.write(struct.pack(">h", data)) + + class Builder(AbsBuilder): + """ """ + login_credentials: Authentication.LoginCredentials = None + + def blob(self, username: str, blob: bytes) -> Session.Builder: + """ + + :param username: str: + :param blob: bytes: + + """ + if self.device_id is None: + raise TypeError("You must specify the device ID first.") + self.login_credentials = self.decrypt_blob(self.device_id, + username, blob) + return self + + def decrypt_blob( + self, device_id: str, username: str, + encrypted_blob: bytes) -> Authentication.LoginCredentials: + """ + + :param device_id: str: + :param username: str: + :param encrypted_blob: bytes: + + """ + encrypted_blob = base64.b64decode(encrypted_blob) + sha1 = SHA1.new() + sha1.update(device_id.encode()) + secret = sha1.digest() + base_key = PBKDF2(secret, + username.encode(), + 20, + 0x100, + hmac_hash_module=SHA1) + sha1 = SHA1.new() + sha1.update(base_key) + key = sha1.digest() + b"\x00\x00\x00\x14" + aes = AES.new(key, AES.MODE_ECB) + decrypted_blob = bytearray(aes.decrypt(encrypted_blob)) + l = len(decrypted_blob) + for i in range(0, l - 0x10): + decrypted_blob[l - i - 1] ^= decrypted_blob[l - i - 0x11] + blob = io.BytesIO(decrypted_blob) + blob.read(1) + le = self.read_blob_int(blob) + blob.read(le) + blob.read(1) + type_int = self.read_blob_int(blob) + type_ = Authentication.AuthenticationType.Name(type_int) + if type_ is None: + raise IOError( + TypeError( + "Unknown AuthenticationType: {}".format(type_int))) + blob.read(1) + l = self.read_blob_int(blob) + auth_data = blob.read(l) + return Authentication.LoginCredentials( + auth_data=auth_data, + typ=type_, + username=username, + ) + + def read_blob_int(self, buffer: io.BytesIO) -> int: + """ + + :param buffer: io.BytesIO: + + """ + lo = buffer.read(1) + if (int(lo[0]) & 0x80) == 0: + return int(lo[0]) + hi = buffer.read(1) + return int(lo[0]) & 0x7F | int(hi[0]) << 7 + + def stored(self, stored_credentials_str: str): + """Create credential from stored string + + :param stored_credentials_str: str: + :returns: Builder + + """ + try: + obj = json.loads(base64.b64decode(stored_credentials_str)) + except binascii.Error: + pass + except json.JSONDecodeError: + pass + else: + try: + self.login_credentials = Authentication.LoginCredentials( + typ=Authentication.AuthenticationType.Value( + obj["type"]), + username=obj["username"], + auth_data=base64.b64decode(obj["credentials"]), + ) + except KeyError: + pass + return self + + def stored_file(self, + stored_credentials: str = None) -> Session.Builder: + """Create credential from stored file + + :param stored_credentials: str: (Default value = None) + :returns: Builder + + """ + if stored_credentials is None: + stored_credentials = self.conf.stored_credentials_file + if os.path.isfile(stored_credentials): + try: + with open(stored_credentials) as f: + obj = json.load(f) + except json.JSONDecodeError: + pass + else: + try: + self.login_credentials = Authentication.LoginCredentials( + typ=Authentication.AuthenticationType.Value( + obj["type"]), + username=obj["username"], + auth_data=base64.b64decode(obj["credentials"]), + ) + except KeyError: + pass + return self + + def user_pass(self, username: str, password: str) -> Session.Builder: + """Create credential from username and password + + :param username: Spotify's account username + :param username: str: + :param password: str: + :returns: Builder + + """ + self.login_credentials = Authentication.LoginCredentials( + username=username, + typ=Authentication.AuthenticationType.AUTHENTICATION_USER_PASS, + auth_data=password.encode(), + ) + return self + + def create(self) -> Session: + """Create the Session instance + + + :returns: Session instance + + """ + if self.login_credentials is None: + raise RuntimeError("You must select an authentication method.") + session = Session( + Session.Inner( + self.device_type, + self.device_name, + self.preferred_locale, + self.conf, + self.device_id, + ), + ApResolver.get_random_accesspoint(), + ) + session.connect() + session.authenticate(self.login_credentials) + return session + + class Configuration: + """ """ + # Proxy + # proxyEnabled: bool + # proxyType: Proxy.Type + # proxyAddress: str + # proxyPort: int + # proxyAuth: bool + # proxyUsername: str + # proxyPassword: str + + # Cache + cache_enabled: bool + cache_dir: str + do_cache_clean_up: bool + + # Stored credentials + store_credentials: bool + stored_credentials_file: str + + # Fetching + retry_on_chunk_error: bool + + def __init__( + self, + # proxy_enabled: bool, + # proxy_type: Proxy.Type, + # proxy_address: str, + # proxy_port: int, + # proxy_auth: bool, + # proxy_username: str, + # proxy_password: str, + cache_enabled: bool, + cache_dir: str, + do_cache_clean_up: bool, + store_credentials: bool, + stored_credentials_file: str, + retry_on_chunk_error: bool, + ): + # self.proxyEnabled = proxy_enabled + # self.proxyType = proxy_type + # self.proxyAddress = proxy_address + # self.proxyPort = proxy_port + # self.proxyAuth = proxy_auth + # self.proxyUsername = proxy_username + # self.proxyPassword = proxy_password + self.cache_enabled = cache_enabled + self.cache_dir = cache_dir + self.do_cache_clean_up = do_cache_clean_up + self.store_credentials = store_credentials + self.stored_credentials_file = stored_credentials_file + self.retry_on_chunk_error = retry_on_chunk_error + + class Builder: + """ """ + # Proxy + # proxyEnabled: bool = False + # proxyType: Proxy.Type = Proxy.Type.DIRECT + # proxyAddress: str = None + # proxyPort: int = None + # proxyAuth: bool = None + # proxyUsername: str = None + # proxyPassword: str = None + + # Cache + cache_enabled: bool = True + cache_dir: str = os.path.join(os.getcwd(), "cache") + do_cache_clean_up: bool = True + + # Stored credentials + store_credentials: bool = True + stored_credentials_file: str = os.path.join( + os.getcwd(), "credentials.json") + + # Fetching + retry_on_chunk_error: bool = True + + # def set_proxy_enabled( + # self, + # proxy_enabled: bool) -> Session.Configuration.Builder: + # self.proxyEnabled = proxy_enabled + # return self + + # def set_proxy_type( + # self, + # proxy_type: Proxy.Type) -> Session.Configuration.Builder: + # self.proxyType = proxy_type + # return self + + # def set_proxy_address( + # self, proxy_address: str) -> Session.Configuration.Builder: + # self.proxyAddress = proxy_address + # return self + + # def set_proxy_auth( + # self, proxy_auth: bool) -> Session.Configuration.Builder: + # self.proxyAuth = proxy_auth + # return self + + # def set_proxy_username( + # self, + # proxy_username: str) -> Session.Configuration.Builder: + # self.proxyUsername = proxy_username + # return self + + # def set_proxy_password( + # self, + # proxy_password: str) -> Session.Configuration.Builder: + # self.proxyPassword = proxy_password + # return self + + def set_cache_enabled( + self, + cache_enabled: bool) -> Session.Configuration.Builder: + """Set cache_enabled + + :param cache_enabled: bool: + :returns: Builder + + """ + self.cache_enabled = cache_enabled + return self + + def set_cache_dir(self, + cache_dir: str) -> Session.Configuration.Builder: + """Set cache_dir + + :param cache_dir: str: + :returns: Builder + + """ + self.cache_dir = cache_dir + return self + + def set_do_cache_clean_up( + self, + do_cache_clean_up: bool) -> Session.Configuration.Builder: + """Set do_cache_clean_up + + :param do_cache_clean_up: bool: + :returns: Builder + + """ + self.do_cache_clean_up = do_cache_clean_up + return self + + def set_store_credentials( + self, + store_credentials: bool) -> Session.Configuration.Builder: + """Set store_credentials + + :param store_credentials: bool: + :returns: Builder + + """ + self.store_credentials = store_credentials + return self + + def set_stored_credential_file( + self, stored_credential_file: str + ) -> Session.Configuration.Builder: + """Set stored_credential_file + + :param stored_credential_file: str: + :returns: Builder + + """ + self.stored_credentials_file = stored_credential_file + return self + + def set_retry_on_chunk_error( + self, retry_on_chunk_error: bool + ) -> Session.Configuration.Builder: + """Set retry_on_chunk_error + + :param retry_on_chunk_error: bool: + :returns: Builder + + """ + self.retry_on_chunk_error = retry_on_chunk_error + return self + + def build(self) -> Session.Configuration: + """Build Configuration instance + + + :returns: Session.Configuration + + """ + return Session.Configuration( + # self.proxyEnabled, + # self.proxyType, + # self.proxyAddress, + # self.proxyPort, + # self.proxyAuth, + # self.proxyUsername, + # self.proxyPassword, + self.cache_enabled, + self.cache_dir, + self.do_cache_clean_up, + self.store_credentials, + self.stored_credentials_file, + self.retry_on_chunk_error, + ) + + class ConnectionHolder: + """ """ + __buffer: io.BytesIO + __socket: socket.socket + + def __init__(self, sock: socket.socket): + self.__buffer = io.BytesIO() + self.__socket = sock + + @staticmethod + def create(address: str, conf) -> Session.ConnectionHolder: + """Create the ConnectionHolder instance + + :param address: Address to connect + :param address: str: + :param conf: + :returns: ConnectionHolder instance + + """ + ap_address = address.split(":")[0] + ap_port = int(address.split(":")[1]) + sock = socket.socket() + sock.connect((ap_address, ap_port)) + return Session.ConnectionHolder(sock) + + def close(self) -> None: + """Close the connection""" + self.__socket.close() + + def flush(self) -> None: + """Flush data to socket""" + try: + self.__buffer.seek(0) + self.__socket.send(self.__buffer.read()) + self.__buffer = io.BytesIO() + except BrokenPipeError: + pass + + def read(self, length: int) -> bytes: + """Read data from socket + + :param length: int: + :returns: Bytes data from socket + + """ + return self.__socket.recv(length) + + def read_int(self) -> int: + """Read integer from socket + + + :returns: integer from socket + + """ + return struct.unpack(">i", self.read(4))[0] + + def read_short(self) -> int: + """Read short integer from socket + + + :returns: short integer from socket + + """ + return struct.unpack(">h", self.read(2))[0] + + def set_timeout(self, seconds: float) -> None: + """Set socket's timeout + + :param seconds: Number of seconds until timeout + :param seconds: float: + + """ + self.__socket.settimeout(None if seconds == 0 else seconds) + + def write(self, data: bytes) -> None: + """Write data to buffer + + :param data: Bytes to be written + :param data: bytes: + + """ + self.__buffer.write(data) + + def write_int(self, data: int) -> None: + """Write data to buffer + + :param data: Integer to be written + :param data: int: + + """ + self.write(struct.pack(">i", data)) + + def write_short(self, data: int) -> None: + """Write data to buffer + + :param data: Short integer to be written + :param data: int: + + """ + self.write(struct.pack(">h", data)) + + class Inner: + """ """ + device_type: Connect.DeviceType = None + device_name: str + device_id: str + conf = None + preferred_locale: str + + def __init__( + self, + device_type: Connect.DeviceType, + device_name: str, + preferred_locale: str, + conf: Session.Configuration, + device_id: str = None, + ): + self.preferred_locale = preferred_locale + self.conf = conf + self.device_type = device_type + self.device_name = device_name + self.device_id = (device_id if device_id is not None else + util.random_hex_string(40)) + + class Receiver: + """ """ + __session: Session + __thread: threading.Thread + __running: bool = True + + def __init__(self, session): + self.__session = session + self.__thread = threading.Thread(target=self.run) + self.__thread.daemon = True + self.__thread.name = "session-packet-receiver" + self.__thread.start() + + def stop(self) -> None: + """ """ + self.__running = False + + def run(self) -> None: + """Receive Packet thread function""" + self.__session.logger.info("Session.Receiver started") + while self.__running: + packet: Packet + cmd: bytes + try: + packet = self.__session.cipher_pair.receive_encoded( + self.__session.connection) + cmd = Packet.Type.parse(packet.cmd) + if cmd is None: + self.__session.logger.info( + "Skipping unknown command cmd: 0x{}, payload: {}". + format(util.bytes_to_hex(packet.cmd), + packet.payload)) + continue + except (RuntimeError, ConnectionResetError) as ex: + if self.__running: + self.__session.logger.fatal( + "Failed reading packet! {}".format(ex)) + self.__session.reconnect() + break + if not self.__running: + break + if cmd == Packet.Type.ping: + if self.__session.scheduled_reconnect is not None: + self.__session.scheduler.cancel( + self.__session.scheduled_reconnect) + + def anonymous(): + """ """ + self.__session.logger.warning( + "Socket timed out. Reconnecting...") + self.__session.reconnect() + + self.__session.scheduled_reconnect = self.__session.scheduler.enter( + 2 * 60 + 5, 1, anonymous) + self.__session.send(Packet.Type.pong, packet.payload) + elif cmd == Packet.Type.pong_ack: + continue + elif cmd == Packet.Type.country_code: + self.__session.__country_code = packet.payload.decode() + self.__session.logger.info( + "Received country_code: {}".format( + self.__session.__country_code)) + elif cmd == Packet.Type.license_version: + license_version = io.BytesIO(packet.payload) + license_id = struct.unpack(">h", + license_version.read(2))[0] + if license_id != 0: + buffer = license_version.read() + self.__session.logger.info( + "Received license_version: {}, {}".format( + license_id, buffer.decode())) + else: + self.__session.logger.info( + "Received license_version: {}".format(license_id)) + elif cmd == Packet.Type.unknown_0x10: + self.__session.logger.debug("Received 0x10: {}".format( + util.bytes_to_hex(packet.payload))) + elif cmd in [ + Packet.Type.mercury_sub, + Packet.Type.mercury_unsub, + Packet.Type.mercury_event, + Packet.Type.mercury_req, + ]: + self.__session.mercury().dispatch(packet) + elif cmd in [Packet.Type.aes_key, Packet.Type.aes_key_error]: + self.__session.audio_key().dispatch(packet) + elif cmd in [ + Packet.Type.channel_error, Packet.Type.stream_chunk_res + ]: + self.__session.channel().dispatch(packet) + elif cmd == Packet.Type.product_info: + self.__session.parse_product_info(packet.payload) + else: + self.__session.logger.info("Skipping {}".format( + util.bytes_to_hex(cmd))) + + class SpotifyAuthenticationException(Exception): + """ """ + + def __init__(self, login_failed: Keyexchange.APLoginFailed): + super().__init__( + Keyexchange.ErrorCode.Name(login_failed.error_code)) + + +class SearchManager: + """ """ + base_url = "hm://searchview/km/v4/search/" + __session: Session + + def __init__(self, session: Session): + self.__session = session + + def request(self, request: SearchRequest) -> typing.Any: + """ + + :param request: SearchRequest: + + """ + if request.get_username() == "": + request.set_username(self.__session.username()) + if request.get_country() == "": + request.set_country(self.__session.country_code) + if request.get_locale() == "": + request.set_locale(self.__session.preferred_locale()) + response = self.__session.mercury().send_sync( + RawMercuryRequest.new_builder().set_method("GET").set_uri( + request.build_url()).build()) + if response.status_code != 200: + raise SearchManager.SearchException(response.status_code) + return json.loads(response.payload) + + class SearchException(Exception): + """ """ + + def __init__(self, status_code: int): + super().__init__("Search failed with code {}.".format(status_code)) + + class SearchRequest: + """ """ + query: typing.Final[str] + __catalogue = "" + __country = "" + __image_size = "" + __limit = 10 + __locale = "" + __username = "" + + def __init__(self, query: str): + self.query = query + if query == "": + raise TypeError + + def build_url(self) -> str: + """ """ + url = SearchManager.base_url + urllib.parse.quote(self.query) + url += "?entityVersion=2" + url += "&catalogue=" + urllib.parse.quote(self.__catalogue) + url += "&country=" + urllib.parse.quote(self.__country) + url += "&imageSize=" + urllib.parse.quote(self.__image_size) + url += "&limit=" + str(self.__limit) + url += "&locale=" + urllib.parse.quote(self.__locale) + url += "&username=" + urllib.parse.quote(self.__username) + return url + + def get_catalogue(self) -> str: + """ """ + return self.__catalogue + + def get_country(self) -> str: + """ """ + return self.__country + + def get_image_size(self) -> str: + """ """ + return self.__image_size + + def get_limit(self) -> int: + """ """ + return self.__limit + + def get_locale(self) -> str: + """ """ + return self.__locale + + def get_username(self) -> str: + """ """ + return self.__username + + def set_catalogue(self, catalogue: str) -> SearchManager.SearchRequest: + """ + + :param catalogue: str: + + """ + self.__catalogue = catalogue + return self + + def set_country(self, country: str) -> SearchManager.SearchRequest: + """ + + :param country: str: + + """ + self.__country = country + return self + + def set_image_size(self, + image_size: str) -> SearchManager.SearchRequest: + """ + + :param image_size: str: + + """ + self.__image_size = image_size + return self + + def set_limit(self, limit: int) -> SearchManager.SearchRequest: + """ + + :param limit: int: + + """ + self.__limit = limit + return self + + def set_locale(self, locale: str) -> SearchManager.SearchRequest: + """ + + :param locale: str: + + """ + self.__locale = locale + return self + + def set_username(self, username: str) -> SearchManager.SearchRequest: + """ + + :param username: str: + + """ + self.__username = username + return self + + +class TokenProvider: + """ """ + logger = logging.getLogger("Librespot:TokenProvider") + token_expire_threshold = 10 + __session: Session + __tokens: typing.List[StoredToken] = [] + + def __init__(self, session: Session): + self._session = session + + def find_token_with_all_scopes( + self, scopes: typing.List[str]) -> typing.Union[StoredToken, None]: + """ + + :param scopes: typing.List[str]: + + """ + for token in self.__tokens: + if token.has_scopes(scopes): + return token + return None + + def get(self, scope: str) -> str: + """ + + :param scope: str: + + """ + return self.get_token(scope).access_token + + def get_token(self, *scopes) -> StoredToken: + """ + + :param *scopes: + + """ + scopes = list(scopes) + if len(scopes) == 0: + raise RuntimeError("The token doesn't have any scope") + token = self.find_token_with_all_scopes(scopes) + if token is not None: + if token.expired(): + self.__tokens.remove(token) + else: + return token + self.logger.debug( + "Token expired or not suitable, requesting again. scopes: {}, old_token: {}" + .format(scopes, token)) + response = self._session.mercury().send_sync_json( + MercuryRequests.request_token(self._session.device_id(), + ",".join(scopes))) + token = TokenProvider.StoredToken(response) + self.logger.debug( + "Updated token successfully! scopes: {}, new_token: {}".format( + scopes, token)) + self.__tokens.append(token) + return token + + class StoredToken: + """ """ + expires_in: int + access_token: str + scopes: typing.List[str] + timestamp: int + + def __init__(self, obj): + self.timestamp = int(time.time_ns() / 1000) + self.expires_in = obj["expiresIn"] + self.access_token = obj["accessToken"] + self.scopes = obj["scope"] + + def expired(self) -> bool: + """ """ + return self.timestamp + (self.expires_in - TokenProvider. + token_expire_threshold) * 1000 < int( + time.time_ns() / 1000) + + def has_scope(self, scope: str) -> bool: + """ + + :param scope: str: + + """ + for s in self.scopes: + if s == scope: + return True + return False + + def has_scopes(self, sc: typing.List[str]) -> bool: + """ + + :param sc: typing.List[str]: + + """ + for s in sc: + if not self.has_scope(s): + return False + return True diff --git a/resources/lib/librespot/crypto.py b/resources/lib/librespot/crypto.py new file mode 100644 index 0000000..6ae8c23 --- /dev/null +++ b/resources/lib/librespot/crypto.py @@ -0,0 +1,412 @@ +from __future__ import annotations +from Cryptodome import Random +from librespot import util +import io +import re +import struct +import typing + +if typing.TYPE_CHECKING: + from librespot.core import Session + + +class CipherPair: + __receive_cipher: Shannon + __receive_nonce = 0 + __send_cipher: Shannon + __send_nonce = 0 + + def __init__(self, send_key: bytes, receive_key: bytes): + self.__send_cipher = Shannon() + self.__send_cipher.key(send_key) + self.__receive_cipher = Shannon() + self.__receive_cipher.key(receive_key) + + def send_encoded(self, connection: Session.ConnectionHolder, cmd: bytes, + payload: bytes) -> None: + """ + Send decrypted data to the socket + :param connection: + :param cmd: + :param payload: + :return: + """ + self.__send_cipher.nonce(self.__send_nonce) + self.__send_nonce += 1 + buffer = io.BytesIO() + buffer.write(cmd) + buffer.write(struct.pack(">H", len(payload))) + buffer.write(payload) + buffer.seek(0) + contents = self.__send_cipher.encrypt(buffer.read()) + mac = self.__send_cipher.finish(4) + connection.write(contents) + connection.write(mac) + connection.flush() + + def receive_encoded(self, connection: Session.ConnectionHolder) -> Packet: + """ + Receive and parse decrypted data from the socket + Args: + connection: ConnectionHolder + Return: + The parsed packet will be returned + """ + try: + self.__receive_cipher.nonce(self.__receive_nonce) + self.__receive_nonce += 1 + header_bytes = self.__receive_cipher.decrypt(connection.read(3)) + cmd = struct.pack(">s", bytes([header_bytes[0]])) + payload_length = (header_bytes[1] << 8) | (header_bytes[2] & 0xff) + payload_bytes = self.__receive_cipher.decrypt( + connection.read(payload_length)) + mac = connection.read(4) + expected_mac = self.__receive_cipher.finish(4) + if mac != expected_mac: + raise RuntimeError() + return Packet(cmd, payload_bytes) + except IndexError: + raise RuntimeError("Failed to receive packet") + + +class DiffieHellman: + """ + DiffieHellman Keyexchange + """ + __prime = int.from_bytes( + b'\xff\xff\xff\xff\xff\xff\xff\xff\xc9\x0f' + b'\xda\xa2!h\xc24\xc4\xc6b\x8b\x80\xdc\x1c' + b'\xd1)\x02N\x08\x8ag\xcct\x02\x0b\xbe\xa6;' + b'\x13\x9b"QJ\x08y\x8e4\x04\xdd\xef\x95\x19' + b'\xb3\xcd:C\x1b0+\nm\xf2_\x147O\xe15mmQ\xc2' + b'E\xe4\x85\xb5vb^~\xc6\xf4LB\xe9\xa6:6 \xff' + b'\xff\xff\xff\xff\xff\xff\xff', + byteorder="big") + __private_key: int + __public_key: int + + def __init__(self): + key_data = Random.get_random_bytes(0x5f) + self.__private_key = int.from_bytes(key_data, byteorder="big") + self.__public_key = pow(2, self.__private_key, self.__prime) + + def compute_shared_key(self, remote_key_bytes: bytes): + """ + Compute shared_key + """ + remote_key = int.from_bytes(remote_key_bytes, "big") + return pow(remote_key, self.__private_key, self.__prime) + + def private_key(self) -> int: + """ + Return DiffieHellman's private key + Returns: + DiffieHellman's private key + """ + return self.__private_key + + def public_key(self) -> int: + """ + Return DiffieHellman's public key + Returns: + DiffieHellman's public key + """ + return self.__public_key + + def public_key_bytes(self) -> bytes: + """ + Return DiffieHellman's packed public key + Returns: + DiffieHellman's packed public key + """ + return util.int_to_bytes(self.__public_key) + + +class Packet: + cmd: bytes + payload: bytes + + def __init__(self, cmd: bytes, payload: bytes): + self.cmd = cmd + self.payload = payload + + def is_cmd(self, cmd: bytes) -> bool: + return cmd == self.cmd + + class Type: + secret_block = b"\x02" + ping = b"\x04" + stream_chunk = b"\x08" + stream_chunk_res = b"\x09" + channel_error = b"\x0a" + channel_abort = b"\x0b" + request_key = b"\x0c" + aes_key = b"\x0d" + aes_key_error = b"\x0e" + image = b"\x19" + country_code = b"\x1b" + pong = b"\x49" + pong_ack = b"\x4a" + pause = b"\x4b" + product_info = b"\x50" + legacy_welcome = b"\x69" + license_version = b"\x76" + login = b"\xab" + ap_welcome = b"\xac" + auth_failure = b"\xad" + mercury_req = b"\xb2" + mercury_sub = b"\xb3" + mercury_unsub = b"\xb4" + mercury_event = b"\xb5" + track_ended_time = b"\x82" + unknown_data_all_zeros = b"\x1f" + preferred_locale = b"\x74" + unknown_0x4f = b"\x4f" + unknown_0x0f = b"\x0f" + unknown_0x10 = b"\x10" + + @staticmethod + def parse(val: typing.Union[bytes, None]) -> typing.Union[bytes, None]: + for cmd in [ + Packet.Type.__dict__[attr] for attr in Packet.Type.__dict__ + if re.search("__.+?__", attr) is None + and type(Packet.Type.__dict__[attr]) is bytes + ]: + if cmd == val: + return cmd + return None + + @staticmethod + def for_method(method: str) -> bytes: + if method == "SUB": + return Packet.Type.mercury_sub + if method == "UNSUB": + return Packet.Type.mercury_unsub + return Packet.Type.mercury_req + + +class Shannon: + n = 16 + fold = n + initkonst = 0x6996c53a + keyp = 13 + r: list + crc: list + init_r: list + konst: int + sbuf: int + mbuf: int + nbuf: int + + def __init__(self): + self.r = [0 for _ in range(self.n)] + self.crc = [0 for _ in range(self.n)] + self.init_r = [0 for _ in range(self.n)] + + def rotl(self, i: int, distance: int) -> int: + return ((i << distance) | (i >> (32 - distance))) & 0xffffffff + + def sbox(self, i: int) -> int: + i ^= self.rotl(i, 5) | self.rotl(i, 7) + i ^= self.rotl(i, 19) | self.rotl(i, 22) + return i + + def sbox2(self, i: int) -> int: + i ^= self.rotl(i, 7) | self.rotl(i, 22) + i ^= self.rotl(i, 5) | self.rotl(i, 19) + return i + + def cycle(self) -> None: + t: int + t = self.r[12] ^ self.r[13] ^ self.konst + t = self.sbox(t) ^ self.rotl(self.r[0], 1) + for i in range(1, self.n): + self.r[i - 1] = self.r[i] + self.r[self.n - 1] = t + t = self.sbox2(self.r[2] ^ self.r[15]) + self.r[0] ^= t + self.sbuf = t ^ self.r[8] ^ self.r[12] + + def crc_func(self, i: int) -> None: + t: int + t = self.crc[0] ^ self.crc[2] ^ self.crc[15] ^ i + for j in range(1, self.n): + self.crc[j - 1] = self.crc[j] + self.crc[self.n - 1] = t + + def mac_func(self, i: int) -> None: + self.crc_func(i) + self.r[self.keyp] ^= i + + def init_state(self) -> None: + self.r[0] = 1 + self.r[1] = 1 + for i in range(2, self.n): + self.r[i] = self.r[i - 1] + self.r[i - 2] + self.konst = self.initkonst + + def save_state(self) -> None: + for i in range(self.n): + self.init_r[i] = self.r[i] + + def reload_state(self) -> None: + for i in range(self.n): + self.r[i] = self.init_r[i] + + def gen_konst(self) -> None: + self.konst = self.r[0] + + def add_key(self, k: int) -> None: + self.r[self.keyp] ^= k + + def diffuse(self) -> None: + for _ in range(self.fold): + self.cycle() + + def load_key(self, key: bytes) -> None: + i: int + j: int + t: int + padding_size = int((len(key) + 3) / 4) * 4 - len(key) + key = key + (b"\x00" * padding_size) + struct.pack(" None: + self.init_state() + self.load_key(key) + self.gen_konst() + self.save_state() + self.nbuf = 0 + + def nonce(self, nonce: typing.Union[bytes, int]) -> None: + if type(nonce) is int: + nonce = bytes(struct.pack(">I", nonce)) + self.reload_state() + self.konst = self.initkonst + self.load_key(nonce) + self.gen_konst() + self.nbuf = 0 + + def encrypt(self, buffer: bytes, n: int = None) -> bytes: + if n is None: + return self.encrypt(buffer, len(buffer)) + buffer = bytearray(buffer) + i = 0 + j: int + t: int + if self.nbuf != 0: + while self.nbuf != 0 and n != 0: + self.mbuf ^= (buffer[i] & 0xff) << (32 - self.nbuf) + buffer[i] ^= (self.sbuf >> (32 - self.nbuf)) & 0xff + i += 1 + self.nbuf -= 8 + n -= 1 + if self.nbuf != 0: + return b"" + self.mac_func(self.mbuf) + j = n & ~0x03 + while i < j: + self.cycle() + t = ((buffer[i + 3] & 0xFF) << 24) | \ + ((buffer[i + 2] & 0xFF) << 16) | \ + ((buffer[i + 1] & 0xFF) << 8) | \ + (buffer[i] & 0xFF) + self.mac_func(t) + t ^= self.sbuf + buffer[i + 3] = (t >> 24) & 0xFF + buffer[i + 2] = (t >> 16) & 0xFF + buffer[i + 1] = (t >> 8) & 0xFF + buffer[i] = t & 0xFF + i += 4 + n &= 0x03 + if n != 0: + self.cycle() + self.mbuf = 0 + self.nbuf = 32 + while self.nbuf != 0 and n != 0: + self.mbuf ^= (buffer[i] & 0xff) << (32 - self.nbuf) + buffer[i] ^= (self.sbuf >> (32 - self.nbuf)) & 0xff + i += 1 + self.nbuf -= 8 + n -= 1 + return bytes(buffer) + + def decrypt(self, buffer: bytes, n: int = None) -> bytes: + if n is None: + return self.decrypt(buffer, len(buffer)) + buffer = bytearray(buffer) + i = 0 + j: int + t: int + if self.nbuf != 0: + while self.nbuf != 0 and n != 0: + buffer[i] ^= (self.sbuf >> (32 - self.nbuf)) & 0xff + self.mbuf ^= (buffer[i] & 0xff) << (32 - self.nbuf) + i += 1 + self.nbuf -= 8 + n -= 1 + if self.nbuf != 0: + return b"" + self.mac_func(self.mbuf) + j = n & ~0x03 + while i < j: + self.cycle() + t = ((buffer[i + 3] & 0xFF) << 24) | \ + ((buffer[i + 2] & 0xFF) << 16) | \ + ((buffer[i + 1] & 0xFF) << 8) | \ + (buffer[i] & 0xFF) + t ^= self.sbuf + self.mac_func(t) + buffer[i + 3] = (t >> 24) & 0xFF + buffer[i + 2] = (t >> 16) & 0xFF + buffer[i + 1] = (t >> 8) & 0xFF + buffer[i] = t & 0xFF + i += 4 + n &= 0x03 + if n != 0: + self.cycle() + self.mbuf = 0 + self.nbuf = 32 + while self.nbuf != 0 and n != 0: + buffer[i] ^= (self.sbuf >> (32 - self.nbuf)) & 0xff + self.mbuf ^= (buffer[i] & 0xff) << (32 - self.nbuf) + i += 1 + self.nbuf -= 8 + n -= 1 + return bytes(buffer) + + def finish(self, n: int) -> bytes: + buffer = bytearray(4) + i = 0 + j: int + if self.nbuf != 0: + self.mac_func(self.mbuf) + self.cycle() + self.add_key(self.initkonst ^ (self.nbuf << 3)) + self.nbuf = 0 + for j in range(self.n): + self.r[j] ^= self.crc[j] + self.diffuse() + while n > 0: + self.cycle() + if n >= 4: + buffer[i + 3] = (self.sbuf >> 24) & 0xff + buffer[i + 2] = (self.sbuf >> 16) & 0xff + buffer[i + 1] = (self.sbuf >> 8) & 0xff + buffer[i] = self.sbuf & 0xff + n -= 4 + i += 4 + else: + for j in range(n): + buffer[i + j] = (self.sbuf >> (i * 8)) & 0xff + break + return bytes(buffer) diff --git a/resources/lib/librespot/dealer.py b/resources/lib/librespot/dealer.py new file mode 100644 index 0000000..143c256 --- /dev/null +++ b/resources/lib/librespot/dealer.py @@ -0,0 +1,11 @@ +from __future__ import annotations +from librespot.core import ApResolver +from librespot.metadata import AlbumId, ArtistId, EpisodeId, ShowId, TrackId +from librespot.proto import Connect_pb2 as Connect, Metadata_pb2 as Metadata +from librespot.structure import Closeable +import logging +import requests +import typing + +if typing.TYPE_CHECKING: + from librespot.core import Session diff --git a/resources/lib/librespot/mercury.py b/resources/lib/librespot/mercury.py new file mode 100644 index 0000000..450b98f --- /dev/null +++ b/resources/lib/librespot/mercury.py @@ -0,0 +1,394 @@ +from __future__ import annotations +from librespot import util +from librespot.crypto import Packet +from librespot.proto import Mercury_pb2 as Mercury, Pubsub_pb2 as Pubsub +from librespot.structure import Closeable, PacketsReceiver, SubListener +import io +import json +import logging +import queue +import struct +import threading +import typing + +if typing.TYPE_CHECKING: + from librespot.core import Session + + +class JsonMercuryRequest: + request: RawMercuryRequest + + def __init__(self, request: RawMercuryRequest): + self.request = request + + +class MercuryClient(Closeable, PacketsReceiver): + logger = logging.getLogger("Librespot:MercuryClient") + mercury_request_timeout = 3 + __callbacks: typing.Dict[int, Callback] = {} + __remove_callback_lock = threading.Condition() + __partials: typing.Dict[int, typing.List[bytes]] = {} + __seq_holder = 0 + __seq_holder_lock = threading.Condition() + __session: Session + __subscriptions: typing.List[InternalSubListener] = [] + __subscriptions_lock = threading.Condition() + + def __init__(self, session: Session): + self.__session = session + + def close(self) -> None: + """ + Close the MercuryClient instance + """ + if len(self.__subscriptions) != 0: + for listener in self.__subscriptions: + if listener.is_sub: + self.unsubscribe(listener.uri) + else: + self.not_interested_in(listener.listener) + if len(self.__callbacks) != 0: + with self.__remove_callback_lock: + self.__remove_callback_lock.wait(self.mercury_request_timeout) + self.__callbacks.clear() + + def dispatch(self, packet: Packet) -> None: + payload = io.BytesIO(packet.payload) + seq_length = struct.unpack(">H", payload.read(2))[0] + if seq_length == 2: + seq = struct.unpack(">H", payload.read(2))[0] + elif seq_length == 4: + seq = struct.unpack(">i", payload.read(4))[0] + elif seq_length == 8: + seq = struct.unpack(">q", payload.read(8))[0] + else: + raise RuntimeError("Unknown seq length: {}".format(seq_length)) + flags = payload.read(1) + parts = struct.unpack(">H", payload.read(2))[0] + partial = self.__partials.get(seq) + if partial is None or flags == 0: + partial = [] + self.__partials[seq] = partial + self.logger.debug( + "Handling packet, cmd: 0x{}, seq: {}, flags: {}, parts: {}".format( + util.bytes_to_hex(packet.cmd), seq, flags, parts)) + for _ in range(parts): + size = struct.unpack(">H", payload.read(2))[0] + buffer = payload.read(size) + partial.append(buffer) + self.__partials[seq] = partial + if flags != b"\x01": + return + self.__partials.pop(seq) + header = Mercury.Header() + header.ParseFromString(partial[0]) + response = MercuryClient.Response(header, partial) + if packet.is_cmd(Packet.Type.mercury_event): + dispatched = False + with self.__subscriptions_lock: + for sub in self.__subscriptions: + if sub.matches(header.uri): + sub.dispatch(response) + dispatched = True + if not dispatched: + self.logger.debug( + "Couldn't dispatch Mercury event seq: {}, uri: {}, code: {}, payload: {}" + .format(seq, header.uri, header.status_code, + response.payload)) + elif (packet.is_cmd(Packet.Type.mercury_req) + or packet.is_cmd(Packet.Type.mercury_sub) + or packet.is_cmd(Packet.Type.mercury_sub)): + callback = self.__callbacks.get(seq) + self.__callbacks.pop(seq) + if callback is not None: + callback.response(response) + else: + self.logger.warning( + "Skipped Mercury response, seq: {}, uri: {}, code: {}". + format(seq, response.uri, response.status_code)) + with self.__remove_callback_lock: + self.__remove_callback_lock.notify_all() + else: + self.logger.warning( + "Couldn't handle packet, seq: {}, uri: {}, code: {}".format( + seq, header.uri, header.status_code)) + + def interested_in(self, uri: str, listener: SubListener) -> None: + self.__subscriptions.append( + MercuryClient.InternalSubListener(uri, listener, False)) + + def not_interested_in(self, listener: SubListener) -> None: + try: + for subscription in self.__subscriptions: + if subscription.listener is listener: + self.__subscriptions.remove(subscription) + break + except ValueError: + pass + + def send(self, request: RawMercuryRequest, callback) -> int: + """ + Send the Mercury request + Args: + request: RawMercuryRequest + callback: Callback function + Returns: + MercuryClient.Response + """ + buffer = io.BytesIO() + seq: int + with self.__seq_holder_lock: + seq = self.__seq_holder + self.__seq_holder += 1 + self.logger.debug( + "Send Mercury request, seq: {}, uri: {}, method: {}".format( + seq, request.header.uri, request.header.method)) + buffer.write(struct.pack(">H", 4)) + buffer.write(struct.pack(">i", seq)) + buffer.write(b"\x01") + buffer.write(struct.pack(">H", 1 + len(request.payload))) + header_bytes = request.header.SerializeToString() + buffer.write(struct.pack(">H", len(header_bytes))) + buffer.write(header_bytes) + for part in request.payload: + buffer.write(struct.pack(">H", len(part))) + buffer.write(part) + buffer.seek(0) + cmd = Packet.Type.for_method(request.header.method) + self.__session.send(cmd, buffer.read()) + self.__callbacks[seq] = callback + return seq + + def send_sync(self, request: RawMercuryRequest) -> Response: + """ + Send the Mercury request + Args: + request: RawMercuryRequest + Returns: + MercuryClient.Response + """ + callback = MercuryClient.SyncCallback() + seq = self.send(request, callback) + try: + response = callback.wait_response() + if response is None: + raise IOError( + "Request timeout out, {} passed, yet no response. seq: {}". + format(self.mercury_request_timeout, seq)) + return response + except queue.Empty as e: + raise IOError(e) + + def send_sync_json(self, request: JsonMercuryRequest) -> typing.Any: + response = self.send_sync(request.request) + if 200 <= response.status_code < 300: + return json.loads(response.payload) + raise MercuryClient.MercuryException(response) + + def subscribe(self, uri: str, listener: SubListener) -> None: + """ + Subscribe URI + Args: + uri: + listener: + """ + response = self.send_sync(RawMercuryRequest.sub(uri)) + if response.status_code != 200: + raise RuntimeError(response) + if len(response.payload) > 0: + for payload in response.payload: + sub = Pubsub.Subscription() + sub.ParseFromString(payload) + self.__subscriptions.append( + MercuryClient.InternalSubListener(sub.uri, listener, True)) + else: + self.__subscriptions.append( + MercuryClient.InternalSubListener(uri, listener, True)) + self.logger.debug("Subscribed successfully to {}!".format(uri)) + + def unsubscribe(self, uri) -> None: + """ + Unsubscribe URI + Args: + uri: + """ + response = self.send_sync(RawMercuryRequest.unsub(uri)) + if response.status_code != 200: + raise RuntimeError(response) + for subscription in self.__subscriptions: + if subscription.matches(uri): + self.__subscriptions.remove(subscription) + break + self.logger.debug("Unsubscribed successfully from {}!".format(uri)) + + class Callback: + def response(self, response: MercuryClient.Response) -> None: + raise NotImplementedError + + class InternalSubListener: + uri: str + listener: SubListener + is_sub: bool + + def __init__(self, uri: str, listener: SubListener, is_sub: bool): + self.uri = uri + self.listener = listener + self.is_sub = is_sub + + def matches(self, uri: str) -> bool: + """ + Compare with the URI given + Args: + uri: URI to be compared + Returns: + bool + """ + return uri.startswith(self.uri) + + def dispatch(self, response: MercuryClient.Response) -> None: + """ + Dispatch the event response + Args: + response: Response generated by the event + """ + self.listener.event(response) + + class MercuryException(Exception): + code: int + + def __init__(self, response: MercuryClient.Response): + super().__init__("status: {}".format(response.status_code)) + self.code = response.status_code + + class PubSubException(MercuryException): + pass + + class Response: + uri: str + payload: bytes + status_code: int + + def __init__(self, header: Mercury.Header, payload: list[bytes]): + self.uri = header.uri + self.status_code = header.status_code + self.payload = b"".join(payload[1:]) + + class SyncCallback(Callback): + __reference = queue.Queue() + + def response(self, response: MercuryClient.Response) -> None: + """ + Set the response + :param response: + :return: + """ + self.__reference.put(response) + self.__reference.task_done() + + def wait_response(self) -> typing.Any: + return self.__reference.get( + timeout=MercuryClient.mercury_request_timeout) + + +class MercuryRequests: + keymaster_client_id = "65b708073fc0480ea92a077233ca87bd" + + @staticmethod + def get_root_playlists(username: str): + """ + @TODO implement function + """ + + @staticmethod + def request_token(device_id, scope): + return JsonMercuryRequest( + RawMercuryRequest.get( + "hm://keymaster/token/authenticated?scope={}&client_id={}&device_id={}" + .format(scope, MercuryRequests.keymaster_client_id, + device_id))) + + +class RawMercuryRequest: + header: Mercury.Header + payload: typing.List[bytes] + + def __init__(self, header: Mercury.Header, payload: typing.List[bytes]): + self.header = header + self.payload = payload + + @staticmethod + def sub(uri: str): + return RawMercuryRequest.new_builder().set_uri(uri).set_method( + "SUB").build() + + @staticmethod + def unsub(uri: str): + return RawMercuryRequest.new_builder().set_uri(uri).set_method( + "UNSUB").build() + + @staticmethod + def get(uri: str): + return RawMercuryRequest.new_builder().set_uri(uri).set_method( + "GET").build() + + @staticmethod + def send(uri: str, part: bytes): + return RawMercuryRequest.new_builder().set_uri(uri) \ + .add_payload_part(part).set_method("SEND").build() + + @staticmethod + def post(uri: str, part: bytes): + return RawMercuryRequest.new_builder().set_uri(uri) \ + .set_method("POST").add_payload_part(part).build() + + @staticmethod + def new_builder(): + return RawMercuryRequest.Builder() + + class Builder: + header_dict: dict + payload: typing.List[bytes] + + def __init__(self): + self.header_dict = {} + self.payload = [] + + def set_uri(self, uri: str): + self.header_dict["uri"] = uri + return self + + def set_content_type(self, content_type: str): + self.header_dict["content_type"] = content_type + return self + + def set_method(self, method: str): + self.header_dict["method"] = method + return self + + def add_user_field(self, + field: Mercury.UserField = None, + key: str = None, + value: str = None): + if field is None and (key is None or value is None): + return self + try: + self.header_dict["user_fields"] + except KeyError: + self.header_dict["user_fields"] = [] + if field is not None: + self.header_dict["user_fields"].append(field) + if key is not None and value is not None: + self.header_dict["user_fields"].append( + Mercury.UserField(key=key, value=value.encode())) + return self + + def add_payload_part(self, part: bytes): + self.payload.append(part) + return self + + def add_protobuf_payload(self, msg): + return self.add_payload_part(msg) + + def build(self): + return RawMercuryRequest(Mercury.Header(**self.header_dict), + self.payload) diff --git a/resources/lib/librespot/metadata.py b/resources/lib/librespot/metadata.py new file mode 100644 index 0000000..a5e01e9 --- /dev/null +++ b/resources/lib/librespot/metadata.py @@ -0,0 +1,283 @@ +from __future__ import annotations +from librespot import util +from librespot.proto.ContextTrack_pb2 import ContextTrack +from librespot.util import Base62 +import re + + +class SpotifyId: + STATIC_FROM_URI = "fromUri" + STATIC_FROM_BASE62 = "fromBase62" + STATIC_FROM_HEX = "fromHex" + + @staticmethod + def from_base62(base62: str): + raise NotImplementedError + + @staticmethod + def from_hex(hex_str: str): + raise NotImplementedError + + @staticmethod + def from_uri(uri: str): + raise NotImplementedError + + def to_spotify_uri(self) -> str: + raise NotImplementedError + + class SpotifyIdParsingException(Exception): + pass + + +class PlayableId: + base62 = Base62.create_instance_with_inverted_character_set() + + @staticmethod + def from_uri(uri: str) -> PlayableId: + if not PlayableId.is_supported(uri): + return UnsupportedId(uri) + if TrackId.pattern.search(uri) is not None: + return TrackId.from_uri(uri) + if EpisodeId.pattern.search(uri) is not None: + return EpisodeId.from_uri(uri) + raise TypeError("Unknown uri: {}".format(uri)) + + @staticmethod + def is_supported(uri: str): + return (not uri.startswith("spotify:local:") + and not uri == "spotify:delimiter" + and not uri == "spotify:meta:delimiter") + + @staticmethod + def should_play(track: ContextTrack): + return track.metadata_or_default + + def get_gid(self) -> bytes: + raise NotImplementedError + + def hex_id(self) -> str: + raise NotImplementedError + + def to_spotify_uri(self) -> str: + raise NotImplementedError + + +class PlaylistId(SpotifyId): + base62 = Base62.create_instance_with_inverted_character_set() + pattern = re.compile(r"spotify:playlist:(.{22})") + __id: str + + def __init__(self, _id: str): + self.__id = _id + + @staticmethod + def from_uri(uri: str) -> PlaylistId: + matcher = PlaylistId.pattern.search(uri) + if matcher is not None: + playlist_id = matcher.group(1) + return PlaylistId(playlist_id) + raise TypeError("Not a Spotify playlist ID: {}.".format(uri)) + + def id(self) -> str: + return self.__id + + def to_spotify_uri(self) -> str: + return "spotify:playlist:" + self.__id + + +class UnsupportedId(PlayableId): + uri: str + + def __init__(self, uri: str): + self.uri = uri + + def get_gid(self) -> bytes: + raise TypeError() + + def hex_id(self) -> str: + raise TypeError() + + def to_spotify_uri(self) -> str: + return self.uri + + +class AlbumId(SpotifyId): + base62 = Base62.create_instance_with_inverted_character_set() + pattern = re.compile(r"spotify:album:(.{22})") + __hex_id: str + + def __init__(self, hex_id: str): + self.__hex_id = hex_id.lower() + + @staticmethod + def from_uri(uri: str) -> AlbumId: + matcher = AlbumId.pattern.search(uri) + if matcher is not None: + album_id = matcher.group(1) + return AlbumId(util.bytes_to_hex(AlbumId.base62.decode(album_id.encode(), 16))) + raise TypeError("Not a Spotify album ID: {}.".format(uri)) + + @staticmethod + def from_base62(base62: str) -> AlbumId: + return AlbumId(util.bytes_to_hex(AlbumId.base62.decode(base62.encode(), 16))) + + @staticmethod + def from_hex(hex_str: str) -> AlbumId: + return AlbumId(hex_str) + + def to_mercury_uri(self) -> str: + return "hm://metadata/4/album/{}".format(self.__hex_id) + + def hex_id(self) -> str: + return self.__hex_id + + def to_spotify_uri(self) -> str: + return "spotify:album:{}".format( + AlbumId.base62.encode(util.hex_to_bytes(self.__hex_id)).decode()) + + +class ArtistId(SpotifyId): + base62 = Base62.create_instance_with_inverted_character_set() + pattern = re.compile("spotify:artist:(.{22})") + __hex_id: str + + def __init__(self, hex_id: str): + self.__hex_id = hex_id.lower() + + @staticmethod + def from_uri(uri: str) -> ArtistId: + matcher = ArtistId.pattern.search(uri) + if matcher is not None: + artist_id = matcher.group(1) + return ArtistId( + util.bytes_to_hex(ArtistId.base62.decode(artist_id.encode(), 16))) + raise TypeError("Not a Spotify artist ID: {}".format(uri)) + + @staticmethod + def from_base62(base62: str) -> ArtistId: + return ArtistId(util.bytes_to_hex(ArtistId.base62.decode(base62.encode(), 16))) + + @staticmethod + def from_hex(hex_str: str) -> ArtistId: + return ArtistId(hex_str) + + def to_mercury_uri(self) -> str: + return "hm://metadata/4/artist/{}".format(self.__hex_id) + + def to_spotify_uri(self) -> str: + return "spotify:artist:{}".format( + ArtistId.base62.encode(util.hex_to_bytes(self.__hex_id)).decode()) + + def hex_id(self) -> str: + return self.__hex_id + + +class EpisodeId(SpotifyId, PlayableId): + pattern = re.compile(r"spotify:episode:(.{22})") + __hex_id: str + + def __init__(self, hex_id: str): + self.__hex_id = hex_id.lower() + + @staticmethod + def from_uri(uri: str) -> EpisodeId: + matcher = EpisodeId.pattern.search(uri) + if matcher is not None: + episode_id = matcher.group(1) + return EpisodeId( + util.bytes_to_hex(PlayableId.base62.decode(episode_id.encode(), 16))) + raise TypeError("Not a Spotify episode ID: {}".format(uri)) + + @staticmethod + def from_base62(base62: str) -> EpisodeId: + return EpisodeId( + util.bytes_to_hex(PlayableId.base62.decode(base62.encode(), 16))) + + @staticmethod + def from_hex(hex_str: str) -> EpisodeId: + return EpisodeId(hex_str) + + def to_mercury_uri(self) -> str: + return "hm://metadata/4/episode/{}".format(self.__hex_id) + + def to_spotify_uri(self) -> str: + return "Spotify:episode:{}".format( + PlayableId.base62.encode(util.hex_to_bytes(self.__hex_id)).decode()) + + def hex_id(self) -> str: + return self.__hex_id + + def get_gid(self) -> bytes: + return util.hex_to_bytes(self.__hex_id) + + +class ShowId(SpotifyId): + base62 = Base62.create_instance_with_inverted_character_set() + pattern = re.compile("spotify:show:(.{22})") + __hex_id: str + + def __init__(self, hex_id: str): + self.__hex_id = hex_id + + @staticmethod + def from_uri(uri: str) -> ShowId: + matcher = ShowId.pattern.search(uri) + if matcher is not None: + show_id = matcher.group(1) + return ShowId(util.bytes_to_hex(ShowId.base62.decode(show_id.encode(), 16))) + raise TypeError("Not a Spotify show ID: {}".format(uri)) + + @staticmethod + def from_base62(base62: str) -> ShowId: + return ShowId(util.bytes_to_hex(ShowId.base62.decode(base62.encode(), 16))) + + @staticmethod + def from_hex(hex_str: str) -> ShowId: + return ShowId(hex_str) + + def to_mercury_uri(self) -> str: + return "hm://metadata/4/show/{}".format(self.__hex_id) + + def to_spotify_uri(self) -> str: + return "spotify:show:{}".format( + ShowId.base62.encode(util.hex_to_bytes(self.__hex_id)).decode()) + + def hex_id(self) -> str: + return self.__hex_id + + +class TrackId(PlayableId, SpotifyId): + pattern = re.compile("spotify:track:(.{22})") + __hex_id: str + + def __init__(self, hex_id: str): + self.__hex_id = hex_id.lower() + + @staticmethod + def from_uri(uri: str) -> TrackId: + search = TrackId.pattern.search(uri) + if search is not None: + track_id = search.group(1) + return TrackId( + util.bytes_to_hex(PlayableId.base62.decode(track_id.encode(), 16))) + raise RuntimeError("Not a Spotify track ID: {}".format(uri)) + + @staticmethod + def from_base62(base62: str) -> TrackId: + return TrackId(util.bytes_to_hex(PlayableId.base62.decode(base62.encode(), 16))) + + @staticmethod + def from_hex(hex_str: str) -> TrackId: + return TrackId(hex_str) + + def to_mercury_uri(self) -> str: + return "hm://metadata/4/track/{}".format(self.__hex_id) + + def to_spotify_uri(self) -> str: + return "spotify:track:{}".format(TrackId.base62.encode(util.hex_to_bytes(self.__hex_id)).decode()) + + def hex_id(self) -> str: + return self.__hex_id + + def get_gid(self) -> bytes: + return util.hex_to_bytes(self.__hex_id) diff --git a/resources/lib/librespot/proto/Authentication_pb2.py b/resources/lib/librespot/proto/Authentication_pb2.py new file mode 100644 index 0000000..b38baf1 --- /dev/null +++ b/resources/lib/librespot/proto/Authentication_pb2.py @@ -0,0 +1,1984 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: authentication.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name='authentication.proto', + package='spotify', + syntax='proto2', + serialized_options=b'\n\013com.spotify', + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x14\x61uthentication.proto\x12\x07spotify\"\xa2\x03\n\x17\x43lientResponseEncrypted\x12\x34\n\x11login_credentials\x18\n \x02(\x0b\x32\x19.spotify.LoginCredentials\x12\x32\n\x10\x61\x63\x63ount_creation\x18\x14 \x01(\x0e\x32\x18.spotify.AccountCreation\x12?\n\x14\x66ingerprint_response\x18\x1e \x01(\x0b\x32!.spotify.FingerprintResponseUnion\x12-\n\x0bpeer_ticket\x18( \x01(\x0b\x32\x18.spotify.PeerTicketUnion\x12(\n\x0bsystem_info\x18\x32 \x02(\x0b\x32\x13.spotify.SystemInfo\x12\x16\n\x0eplatform_model\x18< \x01(\t\x12\x16\n\x0eversion_string\x18\x46 \x01(\t\x12)\n\x06\x61ppkey\x18P \x01(\x0b\x32\x19.spotify.LibspotifyAppKey\x12(\n\x0b\x63lient_info\x18Z \x01(\x0b\x32\x13.spotify.ClientInfo\"a\n\x10LoginCredentials\x12\x10\n\x08username\x18\n \x01(\t\x12(\n\x03typ\x18\x14 \x02(\x0e\x32\x1b.spotify.AuthenticationType\x12\x11\n\tauth_data\x18\x1e \x01(\x0c\"\x89\x01\n\x18\x46ingerprintResponseUnion\x12\x30\n\x05grain\x18\n \x01(\x0b\x32!.spotify.FingerprintGrainResponse\x12;\n\x0bhmac_ripemd\x18\x14 \x01(\x0b\x32&.spotify.FingerprintHmacRipemdResponse\"1\n\x18\x46ingerprintGrainResponse\x12\x15\n\rencrypted_key\x18\n \x02(\x0c\"-\n\x1d\x46ingerprintHmacRipemdResponse\x12\x0c\n\x04hmac\x18\n \x02(\x0c\"o\n\x0fPeerTicketUnion\x12\x30\n\npublic_key\x18\n \x01(\x0b\x32\x1c.spotify.PeerTicketPublicKey\x12*\n\nold_ticket\x18\x14 \x01(\x0b\x32\x16.spotify.PeerTicketOld\")\n\x13PeerTicketPublicKey\x12\x12\n\npublic_key\x18\n \x02(\x0c\"C\n\rPeerTicketOld\x12\x13\n\x0bpeer_ticket\x18\n \x02(\x0c\x12\x1d\n\x15peer_ticket_signature\x18\x14 \x02(\x0c\"\x81\x02\n\nSystemInfo\x12&\n\ncpu_family\x18\n \x02(\x0e\x32\x12.spotify.CpuFamily\x12\x13\n\x0b\x63pu_subtype\x18\x14 \x01(\r\x12\x0f\n\x07\x63pu_ext\x18\x1e \x01(\r\x12\x1d\n\x05\x62rand\x18( \x01(\x0e\x32\x0e.spotify.Brand\x12\x13\n\x0b\x62rand_flags\x18\x32 \x01(\r\x12\x17\n\x02os\x18< \x02(\x0e\x32\x0b.spotify.Os\x12\x12\n\nos_version\x18\x46 \x01(\r\x12\x0e\n\x06os_ext\x18P \x01(\r\x12!\n\x19system_information_string\x18Z \x01(\t\x12\x11\n\tdevice_id\x18\x64 \x01(\t\"p\n\x10LibspotifyAppKey\x12\x0f\n\x07version\x18\x01 \x02(\r\x12\x0e\n\x06\x64\x65vkey\x18\x02 \x02(\x0c\x12\x11\n\tsignature\x18\x03 \x02(\x0c\x12\x11\n\tuseragent\x18\x04 \x02(\t\x12\x15\n\rcallback_hash\x18\x05 \x02(\x0c\"X\n\nClientInfo\x12\x0f\n\x07limited\x18\x01 \x01(\x08\x12\'\n\x02\x66\x62\x18\x02 \x01(\x0b\x32\x1b.spotify.ClientInfoFacebook\x12\x10\n\x08language\x18\x03 \x01(\t\"(\n\x12\x43lientInfoFacebook\x12\x12\n\nmachine_id\x18\x01 \x01(\t\"\xe9\x02\n\tAPWelcome\x12\x1a\n\x12\x63\x61nonical_username\x18\n \x02(\t\x12\x34\n\x16\x61\x63\x63ount_type_logged_in\x18\x14 \x02(\x0e\x32\x14.spotify.AccountType\x12\x38\n\x1a\x63redentials_type_logged_in\x18\x19 \x02(\x0e\x32\x14.spotify.AccountType\x12\x43\n\x1ereusable_auth_credentials_type\x18\x1e \x02(\x0e\x32\x1b.spotify.AuthenticationType\x12!\n\x19reusable_auth_credentials\x18( \x02(\x0c\x12\x12\n\nlfs_secret\x18\x32 \x01(\x0c\x12*\n\x0c\x61\x63\x63ount_info\x18< \x01(\x0b\x32\x14.spotify.AccountInfo\x12(\n\x02\x66\x62\x18\x46 \x01(\x0b\x32\x1c.spotify.AccountInfoFacebook\"k\n\x0b\x41\x63\x63ountInfo\x12,\n\x07spotify\x18\x01 \x01(\x0b\x32\x1b.spotify.AccountInfoSpotify\x12.\n\x08\x66\x61\x63\x65\x62ook\x18\x02 \x01(\x0b\x32\x1c.spotify.AccountInfoFacebook\"\x14\n\x12\x41\x63\x63ountInfoSpotify\"?\n\x13\x41\x63\x63ountInfoFacebook\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x01 \x01(\t\x12\x12\n\nmachine_id\x18\x02 \x01(\t*\xd6\x01\n\x12\x41uthenticationType\x12\x1c\n\x18\x41UTHENTICATION_USER_PASS\x10\x00\x12-\n)AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS\x10\x01\x12.\n*AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS\x10\x02\x12 \n\x1c\x41UTHENTICATION_SPOTIFY_TOKEN\x10\x03\x12!\n\x1d\x41UTHENTICATION_FACEBOOK_TOKEN\x10\x04*Y\n\x0f\x41\x63\x63ountCreation\x12\"\n\x1e\x41\x43\x43OUNT_CREATION_ALWAYS_PROMPT\x10\x01\x12\"\n\x1e\x41\x43\x43OUNT_CREATION_ALWAYS_CREATE\x10\x03*\x9d\x01\n\tCpuFamily\x12\x0f\n\x0b\x43PU_UNKNOWN\x10\x00\x12\x0b\n\x07\x43PU_X86\x10\x01\x12\x0e\n\nCPU_X86_64\x10\x02\x12\x0b\n\x07\x43PU_PPC\x10\x03\x12\x0e\n\nCPU_PPC_64\x10\x04\x12\x0b\n\x07\x43PU_ARM\x10\x05\x12\x0c\n\x08\x43PU_IA64\x10\x06\x12\n\n\x06\x43PU_SH\x10\x07\x12\x0c\n\x08\x43PU_MIPS\x10\x08\x12\x10\n\x0c\x43PU_BLACKFIN\x10\t*K\n\x05\x42rand\x12\x13\n\x0f\x42RAND_UNBRANDED\x10\x00\x12\r\n\tBRAND_INQ\x10\x01\x12\r\n\tBRAND_HTC\x10\x02\x12\x0f\n\x0b\x42RAND_NOKIA\x10\x03*\xd1\x02\n\x02Os\x12\x0e\n\nOS_UNKNOWN\x10\x00\x12\x0e\n\nOS_WINDOWS\x10\x01\x12\n\n\x06OS_OSX\x10\x02\x12\r\n\tOS_IPHONE\x10\x03\x12\n\n\x06OS_S60\x10\x04\x12\x0c\n\x08OS_LINUX\x10\x05\x12\x11\n\rOS_WINDOWS_CE\x10\x06\x12\x0e\n\nOS_ANDROID\x10\x07\x12\x0b\n\x07OS_PALM\x10\x08\x12\x0e\n\nOS_FREEBSD\x10\t\x12\x11\n\rOS_BLACKBERRY\x10\n\x12\x0c\n\x08OS_SONOS\x10\x0b\x12\x0f\n\x0bOS_LOGITECH\x10\x0c\x12\n\n\x06OS_WP7\x10\r\x12\x0c\n\x08OS_ONKYO\x10\x0e\x12\x0e\n\nOS_PHILIPS\x10\x0f\x12\t\n\x05OS_WD\x10\x10\x12\x0c\n\x08OS_VOLVO\x10\x11\x12\x0b\n\x07OS_TIVO\x10\x12\x12\x0b\n\x07OS_AWOX\x10\x13\x12\x0c\n\x08OS_MEEGO\x10\x14\x12\r\n\tOS_QNXNTO\x10\x15\x12\n\n\x06OS_BCO\x10\x16*(\n\x0b\x41\x63\x63ountType\x12\x0b\n\x07Spotify\x10\x00\x12\x0c\n\x08\x46\x61\x63\x65\x62ook\x10\x01\x42\r\n\x0b\x63om.spotify' +) + +_AUTHENTICATIONTYPE = _descriptor.EnumDescriptor( + name='AuthenticationType', + full_name='spotify.AuthenticationType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='AUTHENTICATION_USER_PASS', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AUTHENTICATION_SPOTIFY_TOKEN', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AUTHENTICATION_FACEBOOK_TOKEN', + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2083, + serialized_end=2297, +) +_sym_db.RegisterEnumDescriptor(_AUTHENTICATIONTYPE) + +AuthenticationType = enum_type_wrapper.EnumTypeWrapper(_AUTHENTICATIONTYPE) +_ACCOUNTCREATION = _descriptor.EnumDescriptor( + name='AccountCreation', + full_name='spotify.AccountCreation', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='ACCOUNT_CREATION_ALWAYS_PROMPT', + index=0, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ACCOUNT_CREATION_ALWAYS_CREATE', + index=1, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2299, + serialized_end=2388, +) +_sym_db.RegisterEnumDescriptor(_ACCOUNTCREATION) + +AccountCreation = enum_type_wrapper.EnumTypeWrapper(_ACCOUNTCREATION) +_CPUFAMILY = _descriptor.EnumDescriptor( + name='CpuFamily', + full_name='spotify.CpuFamily', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='CPU_UNKNOWN', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CPU_X86', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CPU_X86_64', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CPU_PPC', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CPU_PPC_64', + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CPU_ARM', + index=5, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CPU_IA64', + index=6, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CPU_SH', + index=7, + number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CPU_MIPS', + index=8, + number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CPU_BLACKFIN', + index=9, + number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2391, + serialized_end=2548, +) +_sym_db.RegisterEnumDescriptor(_CPUFAMILY) + +CpuFamily = enum_type_wrapper.EnumTypeWrapper(_CPUFAMILY) +_BRAND = _descriptor.EnumDescriptor( + name='Brand', + full_name='spotify.Brand', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='BRAND_UNBRANDED', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BRAND_INQ', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BRAND_HTC', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BRAND_NOKIA', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2550, + serialized_end=2625, +) +_sym_db.RegisterEnumDescriptor(_BRAND) + +Brand = enum_type_wrapper.EnumTypeWrapper(_BRAND) +_OS = _descriptor.EnumDescriptor( + name='Os', + full_name='spotify.Os', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='OS_UNKNOWN', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_WINDOWS', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_OSX', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_IPHONE', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_S60', + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_LINUX', + index=5, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_WINDOWS_CE', + index=6, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_ANDROID', + index=7, + number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_PALM', + index=8, + number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_FREEBSD', + index=9, + number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_BLACKBERRY', + index=10, + number=10, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_SONOS', + index=11, + number=11, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_LOGITECH', + index=12, + number=12, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_WP7', + index=13, + number=13, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_ONKYO', + index=14, + number=14, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_PHILIPS', + index=15, + number=15, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_WD', + index=16, + number=16, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_VOLVO', + index=17, + number=17, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_TIVO', + index=18, + number=18, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_AWOX', + index=19, + number=19, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_MEEGO', + index=20, + number=20, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_QNXNTO', + index=21, + number=21, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OS_BCO', + index=22, + number=22, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2628, + serialized_end=2965, +) +_sym_db.RegisterEnumDescriptor(_OS) + +Os = enum_type_wrapper.EnumTypeWrapper(_OS) +_ACCOUNTTYPE = _descriptor.EnumDescriptor( + name='AccountType', + full_name='spotify.AccountType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='Spotify', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='Facebook', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2967, + serialized_end=3007, +) +_sym_db.RegisterEnumDescriptor(_ACCOUNTTYPE) + +AccountType = enum_type_wrapper.EnumTypeWrapper(_ACCOUNTTYPE) +AUTHENTICATION_USER_PASS = 0 +AUTHENTICATION_STORED_SPOTIFY_CREDENTIALS = 1 +AUTHENTICATION_STORED_FACEBOOK_CREDENTIALS = 2 +AUTHENTICATION_SPOTIFY_TOKEN = 3 +AUTHENTICATION_FACEBOOK_TOKEN = 4 +ACCOUNT_CREATION_ALWAYS_PROMPT = 1 +ACCOUNT_CREATION_ALWAYS_CREATE = 3 +CPU_UNKNOWN = 0 +CPU_X86 = 1 +CPU_X86_64 = 2 +CPU_PPC = 3 +CPU_PPC_64 = 4 +CPU_ARM = 5 +CPU_IA64 = 6 +CPU_SH = 7 +CPU_MIPS = 8 +CPU_BLACKFIN = 9 +BRAND_UNBRANDED = 0 +BRAND_INQ = 1 +BRAND_HTC = 2 +BRAND_NOKIA = 3 +OS_UNKNOWN = 0 +OS_WINDOWS = 1 +OS_OSX = 2 +OS_IPHONE = 3 +OS_S60 = 4 +OS_LINUX = 5 +OS_WINDOWS_CE = 6 +OS_ANDROID = 7 +OS_PALM = 8 +OS_FREEBSD = 9 +OS_BLACKBERRY = 10 +OS_SONOS = 11 +OS_LOGITECH = 12 +OS_WP7 = 13 +OS_ONKYO = 14 +OS_PHILIPS = 15 +OS_WD = 16 +OS_VOLVO = 17 +OS_TIVO = 18 +OS_AWOX = 19 +OS_MEEGO = 20 +OS_QNXNTO = 21 +OS_BCO = 22 +Spotify = 0 +Facebook = 1 + +_CLIENTRESPONSEENCRYPTED = _descriptor.Descriptor( + name='ClientResponseEncrypted', + full_name='spotify.ClientResponseEncrypted', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='login_credentials', + full_name='spotify.ClientResponseEncrypted.login_credentials', + index=0, + number=10, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='account_creation', + full_name='spotify.ClientResponseEncrypted.account_creation', + index=1, + number=20, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=1, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='fingerprint_response', + full_name='spotify.ClientResponseEncrypted.fingerprint_response', + index=2, + number=30, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='peer_ticket', + full_name='spotify.ClientResponseEncrypted.peer_ticket', + index=3, + number=40, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='system_info', + full_name='spotify.ClientResponseEncrypted.system_info', + index=4, + number=50, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='platform_model', + full_name='spotify.ClientResponseEncrypted.platform_model', + index=5, + number=60, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='version_string', + full_name='spotify.ClientResponseEncrypted.version_string', + index=6, + number=70, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='appkey', + full_name='spotify.ClientResponseEncrypted.appkey', + index=7, + number=80, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_info', + full_name='spotify.ClientResponseEncrypted.client_info', + index=8, + number=90, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=34, + serialized_end=452, +) + +_LOGINCREDENTIALS = _descriptor.Descriptor( + name='LoginCredentials', + full_name='spotify.LoginCredentials', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='username', + full_name='spotify.LoginCredentials.username', + index=0, + number=10, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='typ', + full_name='spotify.LoginCredentials.typ', + index=1, + number=20, + type=14, + cpp_type=8, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='auth_data', + full_name='spotify.LoginCredentials.auth_data', + index=2, + number=30, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=454, + serialized_end=551, +) + +_FINGERPRINTRESPONSEUNION = _descriptor.Descriptor( + name='FingerprintResponseUnion', + full_name='spotify.FingerprintResponseUnion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='grain', + full_name='spotify.FingerprintResponseUnion.grain', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='hmac_ripemd', + full_name='spotify.FingerprintResponseUnion.hmac_ripemd', + index=1, + number=20, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=554, + serialized_end=691, +) + +_FINGERPRINTGRAINRESPONSE = _descriptor.Descriptor( + name='FingerprintGrainResponse', + full_name='spotify.FingerprintGrainResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='encrypted_key', + full_name='spotify.FingerprintGrainResponse.encrypted_key', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=693, + serialized_end=742, +) + +_FINGERPRINTHMACRIPEMDRESPONSE = _descriptor.Descriptor( + name='FingerprintHmacRipemdResponse', + full_name='spotify.FingerprintHmacRipemdResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='hmac', + full_name='spotify.FingerprintHmacRipemdResponse.hmac', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=744, + serialized_end=789, +) + +_PEERTICKETUNION = _descriptor.Descriptor( + name='PeerTicketUnion', + full_name='spotify.PeerTicketUnion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='public_key', + full_name='spotify.PeerTicketUnion.public_key', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='old_ticket', + full_name='spotify.PeerTicketUnion.old_ticket', + index=1, + number=20, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=791, + serialized_end=902, +) + +_PEERTICKETPUBLICKEY = _descriptor.Descriptor( + name='PeerTicketPublicKey', + full_name='spotify.PeerTicketPublicKey', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='public_key', + full_name='spotify.PeerTicketPublicKey.public_key', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=904, + serialized_end=945, +) + +_PEERTICKETOLD = _descriptor.Descriptor( + name='PeerTicketOld', + full_name='spotify.PeerTicketOld', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='peer_ticket', + full_name='spotify.PeerTicketOld.peer_ticket', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='peer_ticket_signature', + full_name='spotify.PeerTicketOld.peer_ticket_signature', + index=1, + number=20, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=947, + serialized_end=1014, +) + +_SYSTEMINFO = _descriptor.Descriptor( + name='SystemInfo', + full_name='spotify.SystemInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='cpu_family', + full_name='spotify.SystemInfo.cpu_family', + index=0, + number=10, + type=14, + cpp_type=8, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cpu_subtype', + full_name='spotify.SystemInfo.cpu_subtype', + index=1, + number=20, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cpu_ext', + full_name='spotify.SystemInfo.cpu_ext', + index=2, + number=30, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='brand', + full_name='spotify.SystemInfo.brand', + index=3, + number=40, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='brand_flags', + full_name='spotify.SystemInfo.brand_flags', + index=4, + number=50, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='os', + full_name='spotify.SystemInfo.os', + index=5, + number=60, + type=14, + cpp_type=8, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='os_version', + full_name='spotify.SystemInfo.os_version', + index=6, + number=70, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='os_ext', + full_name='spotify.SystemInfo.os_ext', + index=7, + number=80, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='system_information_string', + full_name='spotify.SystemInfo.system_information_string', + index=8, + number=90, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='device_id', + full_name='spotify.SystemInfo.device_id', + index=9, + number=100, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1017, + serialized_end=1274, +) + +_LIBSPOTIFYAPPKEY = _descriptor.Descriptor( + name='LibspotifyAppKey', + full_name='spotify.LibspotifyAppKey', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='version', + full_name='spotify.LibspotifyAppKey.version', + index=0, + number=1, + type=13, + cpp_type=3, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='devkey', + full_name='spotify.LibspotifyAppKey.devkey', + index=1, + number=2, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='signature', + full_name='spotify.LibspotifyAppKey.signature', + index=2, + number=3, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='useragent', + full_name='spotify.LibspotifyAppKey.useragent', + index=3, + number=4, + type=9, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='callback_hash', + full_name='spotify.LibspotifyAppKey.callback_hash', + index=4, + number=5, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1276, + serialized_end=1388, +) + +_CLIENTINFO = _descriptor.Descriptor( + name='ClientInfo', + full_name='spotify.ClientInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='limited', + full_name='spotify.ClientInfo.limited', + index=0, + number=1, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='fb', + full_name='spotify.ClientInfo.fb', + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='language', + full_name='spotify.ClientInfo.language', + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1390, + serialized_end=1478, +) + +_CLIENTINFOFACEBOOK = _descriptor.Descriptor( + name='ClientInfoFacebook', + full_name='spotify.ClientInfoFacebook', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='machine_id', + full_name='spotify.ClientInfoFacebook.machine_id', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1480, + serialized_end=1520, +) + +_APWELCOME = _descriptor.Descriptor( + name='APWelcome', + full_name='spotify.APWelcome', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='canonical_username', + full_name='spotify.APWelcome.canonical_username', + index=0, + number=10, + type=9, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='account_type_logged_in', + full_name='spotify.APWelcome.account_type_logged_in', + index=1, + number=20, + type=14, + cpp_type=8, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='credentials_type_logged_in', + full_name='spotify.APWelcome.credentials_type_logged_in', + index=2, + number=25, + type=14, + cpp_type=8, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reusable_auth_credentials_type', + full_name='spotify.APWelcome.reusable_auth_credentials_type', + index=3, + number=30, + type=14, + cpp_type=8, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reusable_auth_credentials', + full_name='spotify.APWelcome.reusable_auth_credentials', + index=4, + number=40, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='lfs_secret', + full_name='spotify.APWelcome.lfs_secret', + index=5, + number=50, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='account_info', + full_name='spotify.APWelcome.account_info', + index=6, + number=60, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='fb', + full_name='spotify.APWelcome.fb', + index=7, + number=70, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1523, + serialized_end=1884, +) + +_ACCOUNTINFO = _descriptor.Descriptor( + name='AccountInfo', + full_name='spotify.AccountInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='spotify', + full_name='spotify.AccountInfo.spotify', + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='facebook', + full_name='spotify.AccountInfo.facebook', + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1886, + serialized_end=1993, +) + +_ACCOUNTINFOSPOTIFY = _descriptor.Descriptor( + name='AccountInfoSpotify', + full_name='spotify.AccountInfoSpotify', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1995, + serialized_end=2015, +) + +_ACCOUNTINFOFACEBOOK = _descriptor.Descriptor( + name='AccountInfoFacebook', + full_name='spotify.AccountInfoFacebook', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='access_token', + full_name='spotify.AccountInfoFacebook.access_token', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='machine_id', + full_name='spotify.AccountInfoFacebook.machine_id', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2017, + serialized_end=2080, +) + +_CLIENTRESPONSEENCRYPTED.fields_by_name[ + 'login_credentials'].message_type = _LOGINCREDENTIALS +_CLIENTRESPONSEENCRYPTED.fields_by_name[ + 'account_creation'].enum_type = _ACCOUNTCREATION +_CLIENTRESPONSEENCRYPTED.fields_by_name[ + 'fingerprint_response'].message_type = _FINGERPRINTRESPONSEUNION +_CLIENTRESPONSEENCRYPTED.fields_by_name[ + 'peer_ticket'].message_type = _PEERTICKETUNION +_CLIENTRESPONSEENCRYPTED.fields_by_name[ + 'system_info'].message_type = _SYSTEMINFO +_CLIENTRESPONSEENCRYPTED.fields_by_name[ + 'appkey'].message_type = _LIBSPOTIFYAPPKEY +_CLIENTRESPONSEENCRYPTED.fields_by_name[ + 'client_info'].message_type = _CLIENTINFO +_LOGINCREDENTIALS.fields_by_name['typ'].enum_type = _AUTHENTICATIONTYPE +_FINGERPRINTRESPONSEUNION.fields_by_name[ + 'grain'].message_type = _FINGERPRINTGRAINRESPONSE +_FINGERPRINTRESPONSEUNION.fields_by_name[ + 'hmac_ripemd'].message_type = _FINGERPRINTHMACRIPEMDRESPONSE +_PEERTICKETUNION.fields_by_name[ + 'public_key'].message_type = _PEERTICKETPUBLICKEY +_PEERTICKETUNION.fields_by_name['old_ticket'].message_type = _PEERTICKETOLD +_SYSTEMINFO.fields_by_name['cpu_family'].enum_type = _CPUFAMILY +_SYSTEMINFO.fields_by_name['brand'].enum_type = _BRAND +_SYSTEMINFO.fields_by_name['os'].enum_type = _OS +_CLIENTINFO.fields_by_name['fb'].message_type = _CLIENTINFOFACEBOOK +_APWELCOME.fields_by_name['account_type_logged_in'].enum_type = _ACCOUNTTYPE +_APWELCOME.fields_by_name[ + 'credentials_type_logged_in'].enum_type = _ACCOUNTTYPE +_APWELCOME.fields_by_name[ + 'reusable_auth_credentials_type'].enum_type = _AUTHENTICATIONTYPE +_APWELCOME.fields_by_name['account_info'].message_type = _ACCOUNTINFO +_APWELCOME.fields_by_name['fb'].message_type = _ACCOUNTINFOFACEBOOK +_ACCOUNTINFO.fields_by_name['spotify'].message_type = _ACCOUNTINFOSPOTIFY +_ACCOUNTINFO.fields_by_name['facebook'].message_type = _ACCOUNTINFOFACEBOOK +DESCRIPTOR.message_types_by_name[ + 'ClientResponseEncrypted'] = _CLIENTRESPONSEENCRYPTED +DESCRIPTOR.message_types_by_name['LoginCredentials'] = _LOGINCREDENTIALS +DESCRIPTOR.message_types_by_name[ + 'FingerprintResponseUnion'] = _FINGERPRINTRESPONSEUNION +DESCRIPTOR.message_types_by_name[ + 'FingerprintGrainResponse'] = _FINGERPRINTGRAINRESPONSE +DESCRIPTOR.message_types_by_name[ + 'FingerprintHmacRipemdResponse'] = _FINGERPRINTHMACRIPEMDRESPONSE +DESCRIPTOR.message_types_by_name['PeerTicketUnion'] = _PEERTICKETUNION +DESCRIPTOR.message_types_by_name['PeerTicketPublicKey'] = _PEERTICKETPUBLICKEY +DESCRIPTOR.message_types_by_name['PeerTicketOld'] = _PEERTICKETOLD +DESCRIPTOR.message_types_by_name['SystemInfo'] = _SYSTEMINFO +DESCRIPTOR.message_types_by_name['LibspotifyAppKey'] = _LIBSPOTIFYAPPKEY +DESCRIPTOR.message_types_by_name['ClientInfo'] = _CLIENTINFO +DESCRIPTOR.message_types_by_name['ClientInfoFacebook'] = _CLIENTINFOFACEBOOK +DESCRIPTOR.message_types_by_name['APWelcome'] = _APWELCOME +DESCRIPTOR.message_types_by_name['AccountInfo'] = _ACCOUNTINFO +DESCRIPTOR.message_types_by_name['AccountInfoSpotify'] = _ACCOUNTINFOSPOTIFY +DESCRIPTOR.message_types_by_name['AccountInfoFacebook'] = _ACCOUNTINFOFACEBOOK +DESCRIPTOR.enum_types_by_name['AuthenticationType'] = _AUTHENTICATIONTYPE +DESCRIPTOR.enum_types_by_name['AccountCreation'] = _ACCOUNTCREATION +DESCRIPTOR.enum_types_by_name['CpuFamily'] = _CPUFAMILY +DESCRIPTOR.enum_types_by_name['Brand'] = _BRAND +DESCRIPTOR.enum_types_by_name['Os'] = _OS +DESCRIPTOR.enum_types_by_name['AccountType'] = _ACCOUNTTYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ClientResponseEncrypted = _reflection.GeneratedProtocolMessageType( + 'ClientResponseEncrypted', + (_message.Message, ), + { + 'DESCRIPTOR': _CLIENTRESPONSEENCRYPTED, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.ClientResponseEncrypted) + }) +_sym_db.RegisterMessage(ClientResponseEncrypted) + +LoginCredentials = _reflection.GeneratedProtocolMessageType( + 'LoginCredentials', + (_message.Message, ), + { + 'DESCRIPTOR': _LOGINCREDENTIALS, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.LoginCredentials) + }) +_sym_db.RegisterMessage(LoginCredentials) + +FingerprintResponseUnion = _reflection.GeneratedProtocolMessageType( + 'FingerprintResponseUnion', + (_message.Message, ), + { + 'DESCRIPTOR': _FINGERPRINTRESPONSEUNION, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.FingerprintResponseUnion) + }) +_sym_db.RegisterMessage(FingerprintResponseUnion) + +FingerprintGrainResponse = _reflection.GeneratedProtocolMessageType( + 'FingerprintGrainResponse', + (_message.Message, ), + { + 'DESCRIPTOR': _FINGERPRINTGRAINRESPONSE, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.FingerprintGrainResponse) + }) +_sym_db.RegisterMessage(FingerprintGrainResponse) + +FingerprintHmacRipemdResponse = _reflection.GeneratedProtocolMessageType( + 'FingerprintHmacRipemdResponse', + (_message.Message, ), + { + 'DESCRIPTOR': _FINGERPRINTHMACRIPEMDRESPONSE, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.FingerprintHmacRipemdResponse) + }) +_sym_db.RegisterMessage(FingerprintHmacRipemdResponse) + +PeerTicketUnion = _reflection.GeneratedProtocolMessageType( + 'PeerTicketUnion', + (_message.Message, ), + { + 'DESCRIPTOR': _PEERTICKETUNION, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.PeerTicketUnion) + }) +_sym_db.RegisterMessage(PeerTicketUnion) + +PeerTicketPublicKey = _reflection.GeneratedProtocolMessageType( + 'PeerTicketPublicKey', + (_message.Message, ), + { + 'DESCRIPTOR': _PEERTICKETPUBLICKEY, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.PeerTicketPublicKey) + }) +_sym_db.RegisterMessage(PeerTicketPublicKey) + +PeerTicketOld = _reflection.GeneratedProtocolMessageType( + 'PeerTicketOld', + (_message.Message, ), + { + 'DESCRIPTOR': _PEERTICKETOLD, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.PeerTicketOld) + }) +_sym_db.RegisterMessage(PeerTicketOld) + +SystemInfo = _reflection.GeneratedProtocolMessageType( + 'SystemInfo', + (_message.Message, ), + { + 'DESCRIPTOR': _SYSTEMINFO, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.SystemInfo) + }) +_sym_db.RegisterMessage(SystemInfo) + +LibspotifyAppKey = _reflection.GeneratedProtocolMessageType( + 'LibspotifyAppKey', + (_message.Message, ), + { + 'DESCRIPTOR': _LIBSPOTIFYAPPKEY, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.LibspotifyAppKey) + }) +_sym_db.RegisterMessage(LibspotifyAppKey) + +ClientInfo = _reflection.GeneratedProtocolMessageType( + 'ClientInfo', + (_message.Message, ), + { + 'DESCRIPTOR': _CLIENTINFO, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.ClientInfo) + }) +_sym_db.RegisterMessage(ClientInfo) + +ClientInfoFacebook = _reflection.GeneratedProtocolMessageType( + 'ClientInfoFacebook', + (_message.Message, ), + { + 'DESCRIPTOR': _CLIENTINFOFACEBOOK, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.ClientInfoFacebook) + }) +_sym_db.RegisterMessage(ClientInfoFacebook) + +APWelcome = _reflection.GeneratedProtocolMessageType( + 'APWelcome', + (_message.Message, ), + { + 'DESCRIPTOR': _APWELCOME, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.APWelcome) + }) +_sym_db.RegisterMessage(APWelcome) + +AccountInfo = _reflection.GeneratedProtocolMessageType( + 'AccountInfo', + (_message.Message, ), + { + 'DESCRIPTOR': _ACCOUNTINFO, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.AccountInfo) + }) +_sym_db.RegisterMessage(AccountInfo) + +AccountInfoSpotify = _reflection.GeneratedProtocolMessageType( + 'AccountInfoSpotify', + (_message.Message, ), + { + 'DESCRIPTOR': _ACCOUNTINFOSPOTIFY, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.AccountInfoSpotify) + }) +_sym_db.RegisterMessage(AccountInfoSpotify) + +AccountInfoFacebook = _reflection.GeneratedProtocolMessageType( + 'AccountInfoFacebook', + (_message.Message, ), + { + 'DESCRIPTOR': _ACCOUNTINFOFACEBOOK, + '__module__': 'authentication_pb2' + # @@protoc_insertion_point(class_scope:spotify.AccountInfoFacebook) + }) +_sym_db.RegisterMessage(AccountInfoFacebook) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/CanvazMeta_pb2.py b/resources/lib/librespot/proto/CanvazMeta_pb2.py new file mode 100644 index 0000000..3b769fc --- /dev/null +++ b/resources/lib/librespot/proto/CanvazMeta_pb2.py @@ -0,0 +1,91 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: canvaz-meta.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import enum_type_wrapper + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="canvaz-meta.proto", + package="com.spotify.canvaz", + syntax="proto3", + serialized_options=b"\n\022com.spotify.canvazH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b"\n\x11\x63\x61nvaz-meta.proto\x12\x12\x63om.spotify.canvaz*R\n\x04Type\x12\t\n\x05IMAGE\x10\x00\x12\t\n\x05VIDEO\x10\x01\x12\x11\n\rVIDEO_LOOPING\x10\x02\x12\x18\n\x14VIDEO_LOOPING_RANDOM\x10\x03\x12\x07\n\x03GIF\x10\x04\x42\x16\n\x12\x63om.spotify.canvazH\x02\x62\x06proto3", +) + +_TYPE = _descriptor.EnumDescriptor( + name="Type", + full_name="com.spotify.canvaz.Type", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="IMAGE", + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="VIDEO", + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="VIDEO_LOOPING", + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="VIDEO_LOOPING_RANDOM", + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="GIF", + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=41, + serialized_end=123, +) +_sym_db.RegisterEnumDescriptor(_TYPE) + +Type = enum_type_wrapper.EnumTypeWrapper(_TYPE) +IMAGE = 0 +VIDEO = 1 +VIDEO_LOOPING = 2 +VIDEO_LOOPING_RANDOM = 3 +GIF = 4 + +DESCRIPTOR.enum_types_by_name["Type"] = _TYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Canvaz_pb2.py b/resources/lib/librespot/proto/Canvaz_pb2.py new file mode 100644 index 0000000..07f31b4 --- /dev/null +++ b/resources/lib/librespot/proto/Canvaz_pb2.py @@ -0,0 +1,564 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: canvaz.proto +"""Generated protocol buffer code.""" +import CanvazMeta_pb2 as canvaz__meta__pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="canvaz.proto", + package="com.spotify.canvazcache", + syntax="proto3", + serialized_options=b"\n\022com.spotify.canvazH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x0c\x63\x61nvaz.proto\x12\x17\x63om.spotify.canvazcache\x1a\x11\x63\x61nvaz-meta.proto"3\n\x06\x41rtist\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x0e\n\x06\x61vatar\x18\x03 \x01(\t"\xe1\x02\n\x14\x45ntityCanvazResponse\x12\x46\n\x08\x63\x61nvases\x18\x01 \x03(\x0b\x32\x34.com.spotify.canvazcache.EntityCanvazResponse.Canvaz\x12\x16\n\x0ettl_in_seconds\x18\x02 \x01(\x03\x1a\xe8\x01\n\x06\x43\x61nvaz\x12\n\n\x02id\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12\x0f\n\x07\x66ile_id\x18\x03 \x01(\t\x12&\n\x04type\x18\x04 \x01(\x0e\x32\x18.com.spotify.canvaz.Type\x12\x12\n\nentity_uri\x18\x05 \x01(\t\x12/\n\x06\x61rtist\x18\x06 \x01(\x0b\x32\x1f.com.spotify.canvazcache.Artist\x12\x10\n\x08\x65xplicit\x18\x07 \x01(\x08\x12\x13\n\x0buploaded_by\x18\x08 \x01(\t\x12\x0c\n\x04\x65tag\x18\t \x01(\t\x12\x12\n\ncanvas_uri\x18\x0b \x01(\t"\x88\x01\n\x13\x45ntityCanvazRequest\x12\x45\n\x08\x65ntities\x18\x01 \x03(\x0b\x32\x33.com.spotify.canvazcache.EntityCanvazRequest.Entity\x1a*\n\x06\x45ntity\x12\x12\n\nentity_uri\x18\x01 \x01(\t\x12\x0c\n\x04\x65tag\x18\x02 \x01(\tB\x16\n\x12\x63om.spotify.canvazH\x02\x62\x06proto3', + dependencies=[ + canvaz__meta__pb2.DESCRIPTOR, + ], +) + +_ARTIST = _descriptor.Descriptor( + name="Artist", + full_name="com.spotify.canvazcache.Artist", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="uri", + full_name="com.spotify.canvazcache.Artist.uri", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="name", + full_name="com.spotify.canvazcache.Artist.name", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="avatar", + full_name="com.spotify.canvazcache.Artist.avatar", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=60, + serialized_end=111, +) + +_ENTITYCANVAZRESPONSE_CANVAZ = _descriptor.Descriptor( + name="Canvaz", + full_name="com.spotify.canvazcache.EntityCanvazResponse.Canvaz", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="id", + full_name="com.spotify.canvazcache.EntityCanvazResponse.Canvaz.id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="url", + full_name="com.spotify.canvazcache.EntityCanvazResponse.Canvaz.url", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="file_id", + full_name= + "com.spotify.canvazcache.EntityCanvazResponse.Canvaz.file_id", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="type", + full_name= + "com.spotify.canvazcache.EntityCanvazResponse.Canvaz.type", + index=3, + number=4, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="entity_uri", + full_name= + "com.spotify.canvazcache.EntityCanvazResponse.Canvaz.entity_uri", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="artist", + full_name= + "com.spotify.canvazcache.EntityCanvazResponse.Canvaz.artist", + index=5, + number=6, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="explicit", + full_name= + "com.spotify.canvazcache.EntityCanvazResponse.Canvaz.explicit", + index=6, + number=7, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="uploaded_by", + full_name= + "com.spotify.canvazcache.EntityCanvazResponse.Canvaz.uploaded_by", + index=7, + number=8, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="etag", + full_name= + "com.spotify.canvazcache.EntityCanvazResponse.Canvaz.etag", + index=8, + number=9, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="canvas_uri", + full_name= + "com.spotify.canvazcache.EntityCanvazResponse.Canvaz.canvas_uri", + index=9, + number=11, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=235, + serialized_end=467, +) + +_ENTITYCANVAZRESPONSE = _descriptor.Descriptor( + name="EntityCanvazResponse", + full_name="com.spotify.canvazcache.EntityCanvazResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="canvases", + full_name="com.spotify.canvazcache.EntityCanvazResponse.canvases", + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="ttl_in_seconds", + full_name= + "com.spotify.canvazcache.EntityCanvazResponse.ttl_in_seconds", + index=1, + number=2, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[ + _ENTITYCANVAZRESPONSE_CANVAZ, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=114, + serialized_end=467, +) + +_ENTITYCANVAZREQUEST_ENTITY = _descriptor.Descriptor( + name="Entity", + full_name="com.spotify.canvazcache.EntityCanvazRequest.Entity", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="entity_uri", + full_name= + "com.spotify.canvazcache.EntityCanvazRequest.Entity.entity_uri", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="etag", + full_name="com.spotify.canvazcache.EntityCanvazRequest.Entity.etag", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=564, + serialized_end=606, +) + +_ENTITYCANVAZREQUEST = _descriptor.Descriptor( + name="EntityCanvazRequest", + full_name="com.spotify.canvazcache.EntityCanvazRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="entities", + full_name="com.spotify.canvazcache.EntityCanvazRequest.entities", + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[ + _ENTITYCANVAZREQUEST_ENTITY, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=470, + serialized_end=606, +) + +_ENTITYCANVAZRESPONSE_CANVAZ.fields_by_name[ + "type"].enum_type = canvaz__meta__pb2._TYPE +_ENTITYCANVAZRESPONSE_CANVAZ.fields_by_name["artist"].message_type = _ARTIST +_ENTITYCANVAZRESPONSE_CANVAZ.containing_type = _ENTITYCANVAZRESPONSE +_ENTITYCANVAZRESPONSE.fields_by_name[ + "canvases"].message_type = _ENTITYCANVAZRESPONSE_CANVAZ +_ENTITYCANVAZREQUEST_ENTITY.containing_type = _ENTITYCANVAZREQUEST +_ENTITYCANVAZREQUEST.fields_by_name[ + "entities"].message_type = _ENTITYCANVAZREQUEST_ENTITY +DESCRIPTOR.message_types_by_name["Artist"] = _ARTIST +DESCRIPTOR.message_types_by_name[ + "EntityCanvazResponse"] = _ENTITYCANVAZRESPONSE +DESCRIPTOR.message_types_by_name["EntityCanvazRequest"] = _ENTITYCANVAZREQUEST +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Artist = _reflection.GeneratedProtocolMessageType( + "Artist", + (_message.Message, ), + { + "DESCRIPTOR": _ARTIST, + "__module__": "canvaz_pb2" + # @@protoc_insertion_point(class_scope:com.spotify.canvazcache.Artist) + }, +) +_sym_db.RegisterMessage(Artist) + +EntityCanvazResponse = _reflection.GeneratedProtocolMessageType( + "EntityCanvazResponse", + (_message.Message, ), + { + "Canvaz": + _reflection.GeneratedProtocolMessageType( + "Canvaz", + (_message.Message, ), + { + "DESCRIPTOR": _ENTITYCANVAZRESPONSE_CANVAZ, + "__module__": "canvaz_pb2" + # @@protoc_insertion_point(class_scope:com.spotify.canvazcache.EntityCanvazResponse.Canvaz) + }, + ), + "DESCRIPTOR": + _ENTITYCANVAZRESPONSE, + "__module__": + "canvaz_pb2" + # @@protoc_insertion_point(class_scope:com.spotify.canvazcache.EntityCanvazResponse) + }, +) +_sym_db.RegisterMessage(EntityCanvazResponse) +_sym_db.RegisterMessage(EntityCanvazResponse.Canvaz) + +EntityCanvazRequest = _reflection.GeneratedProtocolMessageType( + "EntityCanvazRequest", + (_message.Message, ), + { + "Entity": + _reflection.GeneratedProtocolMessageType( + "Entity", + (_message.Message, ), + { + "DESCRIPTOR": _ENTITYCANVAZREQUEST_ENTITY, + "__module__": "canvaz_pb2" + # @@protoc_insertion_point(class_scope:com.spotify.canvazcache.EntityCanvazRequest.Entity) + }, + ), + "DESCRIPTOR": + _ENTITYCANVAZREQUEST, + "__module__": + "canvaz_pb2" + # @@protoc_insertion_point(class_scope:com.spotify.canvazcache.EntityCanvazRequest) + }, +) +_sym_db.RegisterMessage(EntityCanvazRequest) +_sym_db.RegisterMessage(EntityCanvazRequest.Entity) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/ClientToken_pb2.py b/resources/lib/librespot/proto/ClientToken_pb2.py new file mode 100644 index 0000000..2b7e954 --- /dev/null +++ b/resources/lib/librespot/proto/ClientToken_pb2.py @@ -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) diff --git a/resources/lib/librespot/proto/Connect_pb2.py b/resources/lib/librespot/proto/Connect_pb2.py new file mode 100644 index 0000000..f356a1c --- /dev/null +++ b/resources/lib/librespot/proto/Connect_pb2.py @@ -0,0 +1,2340 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: connect.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +import librespot.proto.Player_pb2 as player__pb2 + +DESCRIPTOR = _descriptor.FileDescriptor( + name='connect.proto', + package='connectstate', + syntax='proto3', + serialized_options=b'\n\030com.spotify.connectstateH\002', + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\rconnect.proto\x12\x0c\x63onnectstate\x1a\x0cplayer.proto\"\x9f\x01\n\rClusterUpdate\x12&\n\x07\x63luster\x18\x01 \x01(\x0b\x32\x15.connectstate.Cluster\x12\x38\n\rupdate_reason\x18\x02 \x01(\x0e\x32!.connectstate.ClusterUpdateReason\x12\x0e\n\x06\x61\x63k_id\x18\x03 \x01(\t\x12\x1c\n\x14\x64\x65vices_that_changed\x18\x04 \x03(\t\"\xa6\x01\n\x06\x44\x65vice\x12-\n\x0b\x64\x65vice_info\x18\x01 \x01(\x0b\x32\x18.connectstate.DeviceInfo\x12/\n\x0cplayer_state\x18\x02 \x01(\x0b\x32\x19.connectstate.PlayerState\x12<\n\x13private_device_info\x18\x03 \x01(\x0b\x32\x1f.connectstate.PrivateDeviceInfo\"\xfa\x01\n\x07\x43luster\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12\x18\n\x10\x61\x63tive_device_id\x18\x02 \x01(\t\x12/\n\x0cplayer_state\x18\x03 \x01(\x0b\x32\x19.connectstate.PlayerState\x12\x31\n\x06\x64\x65vice\x18\x04 \x03(\x0b\x32!.connectstate.Cluster.DeviceEntry\x12\x15\n\rtransfer_data\x18\x05 \x01(\x0c\x1aG\n\x0b\x44\x65viceEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\'\n\x05value\x18\x02 \x01(\x0b\x32\x18.connectstate.DeviceInfo:\x02\x38\x01\"\xa1\x03\n\x0fPutStateRequest\x12\x14\n\x0c\x63\x61llback_url\x18\x01 \x01(\t\x12$\n\x06\x64\x65vice\x18\x02 \x01(\x0b\x32\x14.connectstate.Device\x12-\n\x0bmember_type\x18\x03 \x01(\x0e\x32\x18.connectstate.MemberType\x12\x11\n\tis_active\x18\x04 \x01(\x08\x12\x36\n\x10put_state_reason\x18\x05 \x01(\x0e\x32\x1c.connectstate.PutStateReason\x12\x12\n\nmessage_id\x18\x06 \x01(\r\x12&\n\x1elast_command_sent_by_device_id\x18\x07 \x01(\t\x12\x1f\n\x17last_command_message_id\x18\x08 \x01(\r\x12\x1a\n\x12started_playing_at\x18\t \x01(\x04\x12\x1f\n\x17has_been_playing_for_ms\x18\x0b \x01(\x04\x12\x1d\n\x15\x63lient_side_timestamp\x18\x0c \x01(\x04\x12\x1f\n\x17only_write_player_state\x18\r \x01(\x08\"%\n\x11PrivateDeviceInfo\x12\x10\n\x08platform\x18\x01 \x01(\t\"(\n\x10SubscribeRequest\x12\x14\n\x0c\x63\x61llback_url\x18\x01 \x01(\t\"\xc5\x03\n\nDeviceInfo\x12\x10\n\x08\x63\x61n_play\x18\x01 \x01(\x08\x12\x0e\n\x06volume\x18\x02 \x01(\r\x12\x0c\n\x04name\x18\x03 \x01(\t\x12\x30\n\x0c\x63\x61pabilities\x18\x04 \x01(\x0b\x32\x1a.connectstate.Capabilities\x12\x1f\n\x17\x64\x65vice_software_version\x18\x06 \x01(\t\x12-\n\x0b\x64\x65vice_type\x18\x07 \x01(\x0e\x32\x18.connectstate.DeviceType\x12\x15\n\rspirc_version\x18\t \x01(\t\x12\x11\n\tdevice_id\x18\n \x01(\t\x12\x1a\n\x12is_private_session\x18\x0b \x01(\x08\x12\x19\n\x11is_social_connect\x18\x0c \x01(\x08\x12\x11\n\tclient_id\x18\r \x01(\t\x12\r\n\x05\x62rand\x18\x0e \x01(\t\x12\r\n\x05model\x18\x0f \x01(\t\x12?\n\x0cmetadata_map\x18\x10 \x03(\x0b\x32).connectstate.DeviceInfo.MetadataMapEntry\x1a\x32\n\x10MetadataMapEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xcc\x04\n\x0c\x43\x61pabilities\x12\x15\n\rcan_be_player\x18\x02 \x01(\x08\x12\x19\n\x11restrict_to_local\x18\x03 \x01(\x08\x12\x1a\n\x12gaia_eq_connect_id\x18\x05 \x01(\x08\x12\x17\n\x0fsupports_logout\x18\x06 \x01(\x08\x12\x15\n\ris_observable\x18\x07 \x01(\x08\x12\x14\n\x0cvolume_steps\x18\x08 \x01(\x05\x12\x17\n\x0fsupported_types\x18\t \x03(\t\x12\x14\n\x0c\x63ommand_acks\x18\n \x01(\x08\x12\x17\n\x0fsupports_rename\x18\x0b \x01(\x08\x12\x0e\n\x06hidden\x18\x0c \x01(\x08\x12\x16\n\x0e\x64isable_volume\x18\r \x01(\x08\x12\x18\n\x10\x63onnect_disabled\x18\x0e \x01(\x08\x12\x1c\n\x14supports_playlist_v2\x18\x0f \x01(\x08\x12\x17\n\x0fis_controllable\x18\x10 \x01(\x08\x12\"\n\x1asupports_external_episodes\x18\x11 \x01(\x08\x12%\n\x1dsupports_set_backend_metadata\x18\x12 \x01(\x08\x12!\n\x19supports_transfer_command\x18\x13 \x01(\x08\x12 \n\x18supports_command_request\x18\x14 \x01(\x08\x12\x18\n\x10is_voice_enabled\x18\x15 \x01(\x08\x12\x1f\n\x17needs_full_player_state\x18\x16 \x01(\x08\x12\x1c\n\x14supports_gzip_pushes\x18\x17 \x01(\x08\"+\n\x15\x43onnectCommandOptions\x12\x12\n\nmessage_id\x18\x01 \x01(\x05\"M\n\rLogoutCommand\x12<\n\x0f\x63ommand_options\x18\x01 \x01(\x0b\x32#.connectstate.ConnectCommandOptions\"`\n\x10SetVolumeCommand\x12\x0e\n\x06volume\x18\x01 \x01(\x05\x12<\n\x0f\x63ommand_options\x18\x02 \x01(\x0b\x32#.connectstate.ConnectCommandOptions\"`\n\rRenameCommand\x12\x11\n\trename_to\x18\x01 \x01(\t\x12<\n\x0f\x63ommand_options\x18\x02 \x01(\x0b\x32#.connectstate.ConnectCommandOptions\"\x95\x01\n\x19SetBackendMetadataCommand\x12G\n\x08metadata\x18\x01 \x03(\x0b\x32\x35.connectstate.SetBackendMetadataCommand.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01*\xd4\x01\n\x11SendCommandResult\x12\x1f\n\x1bUNKNOWN_SEND_COMMAND_RESULT\x10\x00\x12\x0b\n\x07SUCCESS\x10\x01\x12\x14\n\x10\x44\x45VICE_NOT_FOUND\x10\x02\x12\x18\n\x14\x43ONTEXT_PLAYER_ERROR\x10\x03\x12\x16\n\x12\x44\x45VICE_DISAPPEARED\x10\x04\x12\x12\n\x0eUPSTREAM_ERROR\x10\x05\x12#\n\x1f\x44\x45VICE_DOES_NOT_SUPPORT_COMMAND\x10\x06\x12\x10\n\x0cRATE_LIMITED\x10\x07*\xb7\x01\n\x0ePutStateReason\x12\x1c\n\x18UNKNOWN_PUT_STATE_REASON\x10\x00\x12\x0f\n\x0bSPIRC_HELLO\x10\x01\x12\x10\n\x0cSPIRC_NOTIFY\x10\x02\x12\x0e\n\nNEW_DEVICE\x10\x03\x12\x18\n\x14PLAYER_STATE_CHANGED\x10\x04\x12\x12\n\x0eVOLUME_CHANGED\x10\x05\x12\x11\n\rPICKER_OPENED\x10\x06\x12\x13\n\x0f\x42\x45\x43\x41ME_INACTIVE\x10\x07*;\n\nMemberType\x12\x0c\n\x08SPIRC_V2\x10\x00\x12\x0c\n\x08SPIRC_V3\x10\x01\x12\x11\n\rCONNECT_STATE\x10\x02*\x84\x01\n\x13\x43lusterUpdateReason\x12!\n\x1dUNKNOWN_CLUSTER_UPDATE_REASON\x10\x00\x12\x17\n\x13\x44\x45VICES_DISAPPEARED\x10\x01\x12\x18\n\x14\x44\x45VICE_STATE_CHANGED\x10\x02\x12\x17\n\x13NEW_DEVICE_APPEARED\x10\x03*\xa0\x02\n\nDeviceType\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x0c\n\x08\x43OMPUTER\x10\x01\x12\n\n\x06TABLET\x10\x02\x12\x0e\n\nSMARTPHONE\x10\x03\x12\x0b\n\x07SPEAKER\x10\x04\x12\x06\n\x02TV\x10\x05\x12\x07\n\x03\x41VR\x10\x06\x12\x07\n\x03STB\x10\x07\x12\x10\n\x0c\x41UDIO_DONGLE\x10\x08\x12\x10\n\x0cGAME_CONSOLE\x10\t\x12\x0e\n\nCAST_VIDEO\x10\n\x12\x0e\n\nCAST_AUDIO\x10\x0b\x12\x0e\n\nAUTOMOBILE\x10\x0c\x12\x0e\n\nSMARTWATCH\x10\r\x12\x0e\n\nCHROMEBOOK\x10\x0e\x12\x13\n\x0fUNKNOWN_SPOTIFY\x10\x64\x12\r\n\tCAR_THING\x10\x65\x12\x0c\n\x08OBSERVER\x10\x66\x12\x0e\n\nHOME_THING\x10gB\x1c\n\x18\x63om.spotify.connectstateH\x02\x62\x06proto3', + dependencies=[ + player__pb2.DESCRIPTOR, + ]) + +_SENDCOMMANDRESULT = _descriptor.EnumDescriptor( + name='SendCommandResult', + full_name='connectstate.SendCommandResult', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='UNKNOWN_SEND_COMMAND_RESULT', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SUCCESS', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICE_NOT_FOUND', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CONTEXT_PLAYER_ERROR', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICE_DISAPPEARED', + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='UPSTREAM_ERROR', + index=5, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICE_DOES_NOT_SUPPORT_COMMAND', + index=6, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='RATE_LIMITED', + index=7, + number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2650, + serialized_end=2862, +) +_sym_db.RegisterEnumDescriptor(_SENDCOMMANDRESULT) + +SendCommandResult = enum_type_wrapper.EnumTypeWrapper(_SENDCOMMANDRESULT) +_PUTSTATEREASON = _descriptor.EnumDescriptor( + name='PutStateReason', + full_name='connectstate.PutStateReason', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='UNKNOWN_PUT_STATE_REASON', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SPIRC_HELLO', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SPIRC_NOTIFY', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='NEW_DEVICE', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLAYER_STATE_CHANGED', + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='VOLUME_CHANGED', + index=5, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PICKER_OPENED', + index=6, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BECAME_INACTIVE', + index=7, + number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2865, + serialized_end=3048, +) +_sym_db.RegisterEnumDescriptor(_PUTSTATEREASON) + +PutStateReason = enum_type_wrapper.EnumTypeWrapper(_PUTSTATEREASON) +_MEMBERTYPE = _descriptor.EnumDescriptor( + name='MemberType', + full_name='connectstate.MemberType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SPIRC_V2', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SPIRC_V3', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CONNECT_STATE', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3050, + serialized_end=3109, +) +_sym_db.RegisterEnumDescriptor(_MEMBERTYPE) + +MemberType = enum_type_wrapper.EnumTypeWrapper(_MEMBERTYPE) +_CLUSTERUPDATEREASON = _descriptor.EnumDescriptor( + name='ClusterUpdateReason', + full_name='connectstate.ClusterUpdateReason', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='UNKNOWN_CLUSTER_UPDATE_REASON', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICES_DISAPPEARED', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='DEVICE_STATE_CHANGED', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='NEW_DEVICE_APPEARED', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3112, + serialized_end=3244, +) +_sym_db.RegisterEnumDescriptor(_CLUSTERUPDATEREASON) + +ClusterUpdateReason = enum_type_wrapper.EnumTypeWrapper(_CLUSTERUPDATEREASON) +_DEVICETYPE = _descriptor.EnumDescriptor( + name='DeviceType', + full_name='connectstate.DeviceType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='UNKNOWN', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='COMPUTER', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TABLET', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SMARTPHONE', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SPEAKER', + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TV', + index=5, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AVR', + index=6, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STB', + index=7, + number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AUDIO_DONGLE', + index=8, + number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='GAME_CONSOLE', + index=9, + number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CAST_VIDEO', + index=10, + number=10, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CAST_AUDIO', + index=11, + number=11, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AUTOMOBILE', + index=12, + number=12, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SMARTWATCH', + index=13, + number=13, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CHROMEBOOK', + index=14, + number=14, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='UNKNOWN_SPOTIFY', + index=15, + number=100, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CAR_THING', + index=16, + number=101, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OBSERVER', + index=17, + number=102, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='HOME_THING', + index=18, + number=103, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3247, + serialized_end=3535, +) +_sym_db.RegisterEnumDescriptor(_DEVICETYPE) + +DeviceType = enum_type_wrapper.EnumTypeWrapper(_DEVICETYPE) +UNKNOWN_SEND_COMMAND_RESULT = 0 +SUCCESS = 1 +DEVICE_NOT_FOUND = 2 +CONTEXT_PLAYER_ERROR = 3 +DEVICE_DISAPPEARED = 4 +UPSTREAM_ERROR = 5 +DEVICE_DOES_NOT_SUPPORT_COMMAND = 6 +RATE_LIMITED = 7 +UNKNOWN_PUT_STATE_REASON = 0 +SPIRC_HELLO = 1 +SPIRC_NOTIFY = 2 +NEW_DEVICE = 3 +PLAYER_STATE_CHANGED = 4 +VOLUME_CHANGED = 5 +PICKER_OPENED = 6 +BECAME_INACTIVE = 7 +SPIRC_V2 = 0 +SPIRC_V3 = 1 +CONNECT_STATE = 2 +UNKNOWN_CLUSTER_UPDATE_REASON = 0 +DEVICES_DISAPPEARED = 1 +DEVICE_STATE_CHANGED = 2 +NEW_DEVICE_APPEARED = 3 +UNKNOWN = 0 +COMPUTER = 1 +TABLET = 2 +SMARTPHONE = 3 +SPEAKER = 4 +TV = 5 +AVR = 6 +STB = 7 +AUDIO_DONGLE = 8 +GAME_CONSOLE = 9 +CAST_VIDEO = 10 +CAST_AUDIO = 11 +AUTOMOBILE = 12 +SMARTWATCH = 13 +CHROMEBOOK = 14 +UNKNOWN_SPOTIFY = 100 +CAR_THING = 101 +OBSERVER = 102 +HOME_THING = 103 + +_CLUSTERUPDATE = _descriptor.Descriptor( + name='ClusterUpdate', + full_name='connectstate.ClusterUpdate', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='cluster', + full_name='connectstate.ClusterUpdate.cluster', + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='update_reason', + full_name='connectstate.ClusterUpdate.update_reason', + index=1, + number=2, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ack_id', + full_name='connectstate.ClusterUpdate.ack_id', + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='devices_that_changed', + full_name='connectstate.ClusterUpdate.devices_that_changed', + index=3, + number=4, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=46, + serialized_end=205, +) + +_DEVICE = _descriptor.Descriptor( + name='Device', + full_name='connectstate.Device', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='device_info', + full_name='connectstate.Device.device_info', + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='player_state', + full_name='connectstate.Device.player_state', + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='private_device_info', + full_name='connectstate.Device.private_device_info', + index=2, + number=3, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=208, + serialized_end=374, +) + +_CLUSTER_DEVICEENTRY = _descriptor.Descriptor( + name='DeviceEntry', + full_name='connectstate.Cluster.DeviceEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', + full_name='connectstate.Cluster.DeviceEntry.key', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', + full_name='connectstate.Cluster.DeviceEntry.value', + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=b'8\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=556, + serialized_end=627, +) + +_CLUSTER = _descriptor.Descriptor( + name='Cluster', + full_name='connectstate.Cluster', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='timestamp', + full_name='connectstate.Cluster.timestamp', + index=0, + number=1, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='active_device_id', + full_name='connectstate.Cluster.active_device_id', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='player_state', + full_name='connectstate.Cluster.player_state', + index=2, + number=3, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='device', + full_name='connectstate.Cluster.device', + index=3, + number=4, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='transfer_data', + full_name='connectstate.Cluster.transfer_data', + index=4, + number=5, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[ + _CLUSTER_DEVICEENTRY, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=377, + serialized_end=627, +) + +_PUTSTATEREQUEST = _descriptor.Descriptor( + name='PutStateRequest', + full_name='connectstate.PutStateRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='callback_url', + full_name='connectstate.PutStateRequest.callback_url', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='device', + full_name='connectstate.PutStateRequest.device', + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='member_type', + full_name='connectstate.PutStateRequest.member_type', + index=2, + number=3, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_active', + full_name='connectstate.PutStateRequest.is_active', + index=3, + number=4, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='put_state_reason', + full_name='connectstate.PutStateRequest.put_state_reason', + index=4, + number=5, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='message_id', + full_name='connectstate.PutStateRequest.message_id', + index=5, + number=6, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='last_command_sent_by_device_id', + full_name= + 'connectstate.PutStateRequest.last_command_sent_by_device_id', + index=6, + number=7, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='last_command_message_id', + full_name='connectstate.PutStateRequest.last_command_message_id', + index=7, + number=8, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='started_playing_at', + full_name='connectstate.PutStateRequest.started_playing_at', + index=8, + number=9, + type=4, + cpp_type=4, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='has_been_playing_for_ms', + full_name='connectstate.PutStateRequest.has_been_playing_for_ms', + index=9, + number=11, + type=4, + cpp_type=4, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_side_timestamp', + full_name='connectstate.PutStateRequest.client_side_timestamp', + index=10, + number=12, + type=4, + cpp_type=4, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='only_write_player_state', + full_name='connectstate.PutStateRequest.only_write_player_state', + index=11, + number=13, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=630, + serialized_end=1047, +) + +_PRIVATEDEVICEINFO = _descriptor.Descriptor( + name='PrivateDeviceInfo', + full_name='connectstate.PrivateDeviceInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='platform', + full_name='connectstate.PrivateDeviceInfo.platform', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1049, + serialized_end=1086, +) + +_SUBSCRIBEREQUEST = _descriptor.Descriptor( + name='SubscribeRequest', + full_name='connectstate.SubscribeRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='callback_url', + full_name='connectstate.SubscribeRequest.callback_url', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1088, + serialized_end=1128, +) + +_DEVICEINFO_METADATAMAPENTRY = _descriptor.Descriptor( + name='MetadataMapEntry', + full_name='connectstate.DeviceInfo.MetadataMapEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', + full_name='connectstate.DeviceInfo.MetadataMapEntry.key', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', + full_name='connectstate.DeviceInfo.MetadataMapEntry.value', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=b'8\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1534, + serialized_end=1584, +) + +_DEVICEINFO = _descriptor.Descriptor( + name='DeviceInfo', + full_name='connectstate.DeviceInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='can_play', + full_name='connectstate.DeviceInfo.can_play', + index=0, + number=1, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='volume', + full_name='connectstate.DeviceInfo.volume', + index=1, + number=2, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='name', + full_name='connectstate.DeviceInfo.name', + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='capabilities', + full_name='connectstate.DeviceInfo.capabilities', + index=3, + number=4, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='device_software_version', + full_name='connectstate.DeviceInfo.device_software_version', + index=4, + number=6, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='device_type', + full_name='connectstate.DeviceInfo.device_type', + index=5, + number=7, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='spirc_version', + full_name='connectstate.DeviceInfo.spirc_version', + index=6, + number=9, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='device_id', + full_name='connectstate.DeviceInfo.device_id', + index=7, + number=10, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_private_session', + full_name='connectstate.DeviceInfo.is_private_session', + index=8, + number=11, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_social_connect', + full_name='connectstate.DeviceInfo.is_social_connect', + index=9, + number=12, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_id', + full_name='connectstate.DeviceInfo.client_id', + index=10, + number=13, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='brand', + full_name='connectstate.DeviceInfo.brand', + index=11, + number=14, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='model', + full_name='connectstate.DeviceInfo.model', + index=12, + number=15, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='metadata_map', + full_name='connectstate.DeviceInfo.metadata_map', + index=13, + number=16, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[ + _DEVICEINFO_METADATAMAPENTRY, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1131, + serialized_end=1584, +) + +_CAPABILITIES = _descriptor.Descriptor( + name='Capabilities', + full_name='connectstate.Capabilities', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='can_be_player', + full_name='connectstate.Capabilities.can_be_player', + index=0, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='restrict_to_local', + full_name='connectstate.Capabilities.restrict_to_local', + index=1, + number=3, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='gaia_eq_connect_id', + full_name='connectstate.Capabilities.gaia_eq_connect_id', + index=2, + number=5, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='supports_logout', + full_name='connectstate.Capabilities.supports_logout', + index=3, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_observable', + full_name='connectstate.Capabilities.is_observable', + index=4, + number=7, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='volume_steps', + full_name='connectstate.Capabilities.volume_steps', + index=5, + number=8, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='supported_types', + full_name='connectstate.Capabilities.supported_types', + index=6, + number=9, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='command_acks', + full_name='connectstate.Capabilities.command_acks', + index=7, + number=10, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='supports_rename', + full_name='connectstate.Capabilities.supports_rename', + index=8, + number=11, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='hidden', + full_name='connectstate.Capabilities.hidden', + index=9, + number=12, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disable_volume', + full_name='connectstate.Capabilities.disable_volume', + index=10, + number=13, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='connect_disabled', + full_name='connectstate.Capabilities.connect_disabled', + index=11, + number=14, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='supports_playlist_v2', + full_name='connectstate.Capabilities.supports_playlist_v2', + index=12, + number=15, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_controllable', + full_name='connectstate.Capabilities.is_controllable', + index=13, + number=16, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='supports_external_episodes', + full_name='connectstate.Capabilities.supports_external_episodes', + index=14, + number=17, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='supports_set_backend_metadata', + full_name='connectstate.Capabilities.supports_set_backend_metadata', + index=15, + number=18, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='supports_transfer_command', + full_name='connectstate.Capabilities.supports_transfer_command', + index=16, + number=19, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='supports_command_request', + full_name='connectstate.Capabilities.supports_command_request', + index=17, + number=20, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_voice_enabled', + full_name='connectstate.Capabilities.is_voice_enabled', + index=18, + number=21, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='needs_full_player_state', + full_name='connectstate.Capabilities.needs_full_player_state', + index=19, + number=22, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='supports_gzip_pushes', + full_name='connectstate.Capabilities.supports_gzip_pushes', + index=20, + number=23, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1587, + serialized_end=2175, +) + +_CONNECTCOMMANDOPTIONS = _descriptor.Descriptor( + name='ConnectCommandOptions', + full_name='connectstate.ConnectCommandOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='message_id', + full_name='connectstate.ConnectCommandOptions.message_id', + index=0, + number=1, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=2177, + serialized_end=2220, +) + +_LOGOUTCOMMAND = _descriptor.Descriptor( + name='LogoutCommand', + full_name='connectstate.LogoutCommand', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='command_options', + full_name='connectstate.LogoutCommand.command_options', + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=2222, + serialized_end=2299, +) + +_SETVOLUMECOMMAND = _descriptor.Descriptor( + name='SetVolumeCommand', + full_name='connectstate.SetVolumeCommand', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='volume', + full_name='connectstate.SetVolumeCommand.volume', + index=0, + number=1, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='command_options', + full_name='connectstate.SetVolumeCommand.command_options', + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=2301, + serialized_end=2397, +) + +_RENAMECOMMAND = _descriptor.Descriptor( + name='RenameCommand', + full_name='connectstate.RenameCommand', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='rename_to', + full_name='connectstate.RenameCommand.rename_to', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='command_options', + full_name='connectstate.RenameCommand.command_options', + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=2399, + serialized_end=2495, +) + +_SETBACKENDMETADATACOMMAND_METADATAENTRY = _descriptor.Descriptor( + name='MetadataEntry', + full_name='connectstate.SetBackendMetadataCommand.MetadataEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', + full_name= + 'connectstate.SetBackendMetadataCommand.MetadataEntry.key', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', + full_name= + 'connectstate.SetBackendMetadataCommand.MetadataEntry.value', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=b'8\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=2600, + serialized_end=2647, +) + +_SETBACKENDMETADATACOMMAND = _descriptor.Descriptor( + name='SetBackendMetadataCommand', + full_name='connectstate.SetBackendMetadataCommand', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='metadata', + full_name='connectstate.SetBackendMetadataCommand.metadata', + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[ + _SETBACKENDMETADATACOMMAND_METADATAENTRY, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=2498, + serialized_end=2647, +) + +_CLUSTERUPDATE.fields_by_name['cluster'].message_type = _CLUSTER +_CLUSTERUPDATE.fields_by_name['update_reason'].enum_type = _CLUSTERUPDATEREASON +_DEVICE.fields_by_name['device_info'].message_type = _DEVICEINFO +_DEVICE.fields_by_name['player_state'].message_type = player__pb2._PLAYERSTATE +_DEVICE.fields_by_name['private_device_info'].message_type = _PRIVATEDEVICEINFO +_CLUSTER_DEVICEENTRY.fields_by_name['value'].message_type = _DEVICEINFO +_CLUSTER_DEVICEENTRY.containing_type = _CLUSTER +_CLUSTER.fields_by_name['player_state'].message_type = player__pb2._PLAYERSTATE +_CLUSTER.fields_by_name['device'].message_type = _CLUSTER_DEVICEENTRY +_PUTSTATEREQUEST.fields_by_name['device'].message_type = _DEVICE +_PUTSTATEREQUEST.fields_by_name['member_type'].enum_type = _MEMBERTYPE +_PUTSTATEREQUEST.fields_by_name['put_state_reason'].enum_type = _PUTSTATEREASON +_DEVICEINFO_METADATAMAPENTRY.containing_type = _DEVICEINFO +_DEVICEINFO.fields_by_name['capabilities'].message_type = _CAPABILITIES +_DEVICEINFO.fields_by_name['device_type'].enum_type = _DEVICETYPE +_DEVICEINFO.fields_by_name[ + 'metadata_map'].message_type = _DEVICEINFO_METADATAMAPENTRY +_LOGOUTCOMMAND.fields_by_name[ + 'command_options'].message_type = _CONNECTCOMMANDOPTIONS +_SETVOLUMECOMMAND.fields_by_name[ + 'command_options'].message_type = _CONNECTCOMMANDOPTIONS +_RENAMECOMMAND.fields_by_name[ + 'command_options'].message_type = _CONNECTCOMMANDOPTIONS +_SETBACKENDMETADATACOMMAND_METADATAENTRY.containing_type = _SETBACKENDMETADATACOMMAND +_SETBACKENDMETADATACOMMAND.fields_by_name[ + 'metadata'].message_type = _SETBACKENDMETADATACOMMAND_METADATAENTRY +DESCRIPTOR.message_types_by_name['ClusterUpdate'] = _CLUSTERUPDATE +DESCRIPTOR.message_types_by_name['Device'] = _DEVICE +DESCRIPTOR.message_types_by_name['Cluster'] = _CLUSTER +DESCRIPTOR.message_types_by_name['PutStateRequest'] = _PUTSTATEREQUEST +DESCRIPTOR.message_types_by_name['PrivateDeviceInfo'] = _PRIVATEDEVICEINFO +DESCRIPTOR.message_types_by_name['SubscribeRequest'] = _SUBSCRIBEREQUEST +DESCRIPTOR.message_types_by_name['DeviceInfo'] = _DEVICEINFO +DESCRIPTOR.message_types_by_name['Capabilities'] = _CAPABILITIES +DESCRIPTOR.message_types_by_name[ + 'ConnectCommandOptions'] = _CONNECTCOMMANDOPTIONS +DESCRIPTOR.message_types_by_name['LogoutCommand'] = _LOGOUTCOMMAND +DESCRIPTOR.message_types_by_name['SetVolumeCommand'] = _SETVOLUMECOMMAND +DESCRIPTOR.message_types_by_name['RenameCommand'] = _RENAMECOMMAND +DESCRIPTOR.message_types_by_name[ + 'SetBackendMetadataCommand'] = _SETBACKENDMETADATACOMMAND +DESCRIPTOR.enum_types_by_name['SendCommandResult'] = _SENDCOMMANDRESULT +DESCRIPTOR.enum_types_by_name['PutStateReason'] = _PUTSTATEREASON +DESCRIPTOR.enum_types_by_name['MemberType'] = _MEMBERTYPE +DESCRIPTOR.enum_types_by_name['ClusterUpdateReason'] = _CLUSTERUPDATEREASON +DESCRIPTOR.enum_types_by_name['DeviceType'] = _DEVICETYPE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ClusterUpdate = _reflection.GeneratedProtocolMessageType( + 'ClusterUpdate', + (_message.Message, ), + { + 'DESCRIPTOR': _CLUSTERUPDATE, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.ClusterUpdate) + }) +_sym_db.RegisterMessage(ClusterUpdate) + +Device = _reflection.GeneratedProtocolMessageType( + 'Device', + (_message.Message, ), + { + 'DESCRIPTOR': _DEVICE, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.Device) + }) +_sym_db.RegisterMessage(Device) + +Cluster = _reflection.GeneratedProtocolMessageType( + 'Cluster', + (_message.Message, ), + { + 'DeviceEntry': + _reflection.GeneratedProtocolMessageType( + 'DeviceEntry', + (_message.Message, ), + { + 'DESCRIPTOR': _CLUSTER_DEVICEENTRY, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.Cluster.DeviceEntry) + }), + 'DESCRIPTOR': + _CLUSTER, + '__module__': + 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.Cluster) + }) +_sym_db.RegisterMessage(Cluster) +_sym_db.RegisterMessage(Cluster.DeviceEntry) + +PutStateRequest = _reflection.GeneratedProtocolMessageType( + 'PutStateRequest', + (_message.Message, ), + { + 'DESCRIPTOR': _PUTSTATEREQUEST, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.PutStateRequest) + }) +_sym_db.RegisterMessage(PutStateRequest) + +PrivateDeviceInfo = _reflection.GeneratedProtocolMessageType( + 'PrivateDeviceInfo', + (_message.Message, ), + { + 'DESCRIPTOR': _PRIVATEDEVICEINFO, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.PrivateDeviceInfo) + }) +_sym_db.RegisterMessage(PrivateDeviceInfo) + +SubscribeRequest = _reflection.GeneratedProtocolMessageType( + 'SubscribeRequest', + (_message.Message, ), + { + 'DESCRIPTOR': _SUBSCRIBEREQUEST, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.SubscribeRequest) + }) +_sym_db.RegisterMessage(SubscribeRequest) + +DeviceInfo = _reflection.GeneratedProtocolMessageType( + 'DeviceInfo', + (_message.Message, ), + { + 'MetadataMapEntry': + _reflection.GeneratedProtocolMessageType( + 'MetadataMapEntry', + (_message.Message, ), + { + 'DESCRIPTOR': _DEVICEINFO_METADATAMAPENTRY, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.DeviceInfo.MetadataMapEntry) + }), + 'DESCRIPTOR': + _DEVICEINFO, + '__module__': + 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.DeviceInfo) + }) +_sym_db.RegisterMessage(DeviceInfo) +_sym_db.RegisterMessage(DeviceInfo.MetadataMapEntry) + +Capabilities = _reflection.GeneratedProtocolMessageType( + 'Capabilities', + (_message.Message, ), + { + 'DESCRIPTOR': _CAPABILITIES, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.Capabilities) + }) +_sym_db.RegisterMessage(Capabilities) + +ConnectCommandOptions = _reflection.GeneratedProtocolMessageType( + 'ConnectCommandOptions', + (_message.Message, ), + { + 'DESCRIPTOR': _CONNECTCOMMANDOPTIONS, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.ConnectCommandOptions) + }) +_sym_db.RegisterMessage(ConnectCommandOptions) + +LogoutCommand = _reflection.GeneratedProtocolMessageType( + 'LogoutCommand', + (_message.Message, ), + { + 'DESCRIPTOR': _LOGOUTCOMMAND, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.LogoutCommand) + }) +_sym_db.RegisterMessage(LogoutCommand) + +SetVolumeCommand = _reflection.GeneratedProtocolMessageType( + 'SetVolumeCommand', + (_message.Message, ), + { + 'DESCRIPTOR': _SETVOLUMECOMMAND, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.SetVolumeCommand) + }) +_sym_db.RegisterMessage(SetVolumeCommand) + +RenameCommand = _reflection.GeneratedProtocolMessageType( + 'RenameCommand', + (_message.Message, ), + { + 'DESCRIPTOR': _RENAMECOMMAND, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.RenameCommand) + }) +_sym_db.RegisterMessage(RenameCommand) + +SetBackendMetadataCommand = _reflection.GeneratedProtocolMessageType( + 'SetBackendMetadataCommand', + (_message.Message, ), + { + 'MetadataEntry': + _reflection.GeneratedProtocolMessageType( + 'MetadataEntry', + (_message.Message, ), + { + 'DESCRIPTOR': _SETBACKENDMETADATACOMMAND_METADATAENTRY, + '__module__': 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.SetBackendMetadataCommand.MetadataEntry) + }), + 'DESCRIPTOR': + _SETBACKENDMETADATACOMMAND, + '__module__': + 'connect_pb2' + # @@protoc_insertion_point(class_scope:connectstate.SetBackendMetadataCommand) + }) +_sym_db.RegisterMessage(SetBackendMetadataCommand) +_sym_db.RegisterMessage(SetBackendMetadataCommand.MetadataEntry) + +DESCRIPTOR._options = None +_CLUSTER_DEVICEENTRY._options = None +_DEVICEINFO_METADATAMAPENTRY._options = None +_SETBACKENDMETADATACOMMAND_METADATAENTRY._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Connectivity_pb2.py b/resources/lib/librespot/proto/Connectivity_pb2.py new file mode 100644 index 0000000..bf17a91 --- /dev/null +++ b/resources/lib/librespot/proto/Connectivity_pb2.py @@ -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) diff --git a/resources/lib/librespot/proto/ContextPage_pb2.py b/resources/lib/librespot/proto/ContextPage_pb2.py new file mode 100644 index 0000000..5256d5a --- /dev/null +++ b/resources/lib/librespot/proto/ContextPage_pb2.py @@ -0,0 +1,239 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: context_page.proto +"""Generated protocol buffer code.""" +import ContextTrack_pb2 as context__track__pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="context_page.proto", + package="spotify.player.proto", + syntax="proto2", + serialized_options=b"\n\023com.spotify.contextH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x12\x63ontext_page.proto\x12\x14spotify.player.proto\x1a\x13\x63ontext_track.proto"\xef\x01\n\x0b\x43ontextPage\x12\x10\n\x08page_url\x18\x01 \x01(\t\x12\x15\n\rnext_page_url\x18\x02 \x01(\t\x12\x41\n\x08metadata\x18\x03 \x03(\x0b\x32/.spotify.player.proto.ContextPage.MetadataEntry\x12\x32\n\x06tracks\x18\x04 \x03(\x0b\x32".spotify.player.proto.ContextTrack\x12\x0f\n\x07loading\x18\x05 \x01(\x08\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x17\n\x13\x63om.spotify.contextH\x02', + dependencies=[ + context__track__pb2.DESCRIPTOR, + ], +) + +_CONTEXTPAGE_METADATAENTRY = _descriptor.Descriptor( + name="MetadataEntry", + full_name="spotify.player.proto.ContextPage.MetadataEntry", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="spotify.player.proto.ContextPage.MetadataEntry.key", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="value", + full_name="spotify.player.proto.ContextPage.MetadataEntry.value", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=b"8\001", + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=258, + serialized_end=305, +) + +_CONTEXTPAGE = _descriptor.Descriptor( + name="ContextPage", + full_name="spotify.player.proto.ContextPage", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="page_url", + full_name="spotify.player.proto.ContextPage.page_url", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="next_page_url", + full_name="spotify.player.proto.ContextPage.next_page_url", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="metadata", + full_name="spotify.player.proto.ContextPage.metadata", + index=2, + number=3, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="tracks", + full_name="spotify.player.proto.ContextPage.tracks", + index=3, + number=4, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="loading", + full_name="spotify.player.proto.ContextPage.loading", + index=4, + number=5, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[ + _CONTEXTPAGE_METADATAENTRY, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=66, + serialized_end=305, +) + +_CONTEXTPAGE_METADATAENTRY.containing_type = _CONTEXTPAGE +_CONTEXTPAGE.fields_by_name[ + "metadata"].message_type = _CONTEXTPAGE_METADATAENTRY +_CONTEXTPAGE.fields_by_name[ + "tracks"].message_type = context__track__pb2._CONTEXTTRACK +DESCRIPTOR.message_types_by_name["ContextPage"] = _CONTEXTPAGE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ContextPage = _reflection.GeneratedProtocolMessageType( + "ContextPage", + (_message.Message, ), + { + "MetadataEntry": + _reflection.GeneratedProtocolMessageType( + "MetadataEntry", + (_message.Message, ), + { + "DESCRIPTOR": _CONTEXTPAGE_METADATAENTRY, + "__module__": "context_page_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.ContextPage.MetadataEntry) + }, + ), + "DESCRIPTOR": + _CONTEXTPAGE, + "__module__": + "context_page_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.ContextPage) + }, +) +_sym_db.RegisterMessage(ContextPage) +_sym_db.RegisterMessage(ContextPage.MetadataEntry) + +DESCRIPTOR._options = None +_CONTEXTPAGE_METADATAENTRY._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/ContextPlayerOptions_pb2.py b/resources/lib/librespot/proto/ContextPlayerOptions_pb2.py new file mode 100644 index 0000000..7b62e05 --- /dev/null +++ b/resources/lib/librespot/proto/ContextPlayerOptions_pb2.py @@ -0,0 +1,215 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: context_player_options.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="context_player_options.proto", + package="spotify.player.proto", + syntax="proto2", + serialized_options=b"\n\023com.spotify.contextH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x1c\x63ontext_player_options.proto\x12\x14spotify.player.proto"e\n\x14\x43ontextPlayerOptions\x12\x19\n\x11shuffling_context\x18\x01 \x01(\x08\x12\x19\n\x11repeating_context\x18\x02 \x01(\x08\x12\x17\n\x0frepeating_track\x18\x03 \x01(\x08"m\n\x1c\x43ontextPlayerOptionOverrides\x12\x19\n\x11shuffling_context\x18\x01 \x01(\x08\x12\x19\n\x11repeating_context\x18\x02 \x01(\x08\x12\x17\n\x0frepeating_track\x18\x03 \x01(\x08\x42\x17\n\x13\x63om.spotify.contextH\x02', +) + +_CONTEXTPLAYEROPTIONS = _descriptor.Descriptor( + name="ContextPlayerOptions", + full_name="spotify.player.proto.ContextPlayerOptions", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="shuffling_context", + full_name= + "spotify.player.proto.ContextPlayerOptions.shuffling_context", + index=0, + number=1, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="repeating_context", + full_name= + "spotify.player.proto.ContextPlayerOptions.repeating_context", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="repeating_track", + full_name= + "spotify.player.proto.ContextPlayerOptions.repeating_track", + index=2, + number=3, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=54, + serialized_end=155, +) + +_CONTEXTPLAYEROPTIONOVERRIDES = _descriptor.Descriptor( + name="ContextPlayerOptionOverrides", + full_name="spotify.player.proto.ContextPlayerOptionOverrides", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="shuffling_context", + full_name= + "spotify.player.proto.ContextPlayerOptionOverrides.shuffling_context", + index=0, + number=1, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="repeating_context", + full_name= + "spotify.player.proto.ContextPlayerOptionOverrides.repeating_context", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="repeating_track", + full_name= + "spotify.player.proto.ContextPlayerOptionOverrides.repeating_track", + index=2, + number=3, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=157, + serialized_end=266, +) + +DESCRIPTOR.message_types_by_name[ + "ContextPlayerOptions"] = _CONTEXTPLAYEROPTIONS +DESCRIPTOR.message_types_by_name[ + "ContextPlayerOptionOverrides"] = _CONTEXTPLAYEROPTIONOVERRIDES +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ContextPlayerOptions = _reflection.GeneratedProtocolMessageType( + "ContextPlayerOptions", + (_message.Message, ), + { + "DESCRIPTOR": _CONTEXTPLAYEROPTIONS, + "__module__": "context_player_options_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.ContextPlayerOptions) + }, +) +_sym_db.RegisterMessage(ContextPlayerOptions) + +ContextPlayerOptionOverrides = _reflection.GeneratedProtocolMessageType( + "ContextPlayerOptionOverrides", + (_message.Message, ), + { + "DESCRIPTOR": _CONTEXTPLAYEROPTIONOVERRIDES, + "__module__": "context_player_options_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.ContextPlayerOptionOverrides) + }, +) +_sym_db.RegisterMessage(ContextPlayerOptionOverrides) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/ContextTrack_pb2.py b/resources/lib/librespot/proto/ContextTrack_pb2.py new file mode 100644 index 0000000..495b4ea --- /dev/null +++ b/resources/lib/librespot/proto/ContextTrack_pb2.py @@ -0,0 +1,214 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: context_track.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="context_track.proto", + package="spotify.player.proto", + syntax="proto2", + serialized_options=b"\n\023com.spotify.contextH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x13\x63ontext_track.proto\x12\x14spotify.player.proto"\xaa\x01\n\x0c\x43ontextTrack\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x0b\n\x03uid\x18\x02 \x01(\t\x12\x0b\n\x03gid\x18\x03 \x01(\x0c\x12\x42\n\x08metadata\x18\x04 \x03(\x0b\x32\x30.spotify.player.proto.ContextTrack.MetadataEntry\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x17\n\x13\x63om.spotify.contextH\x02', +) + +_CONTEXTTRACK_METADATAENTRY = _descriptor.Descriptor( + name="MetadataEntry", + full_name="spotify.player.proto.ContextTrack.MetadataEntry", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="spotify.player.proto.ContextTrack.MetadataEntry.key", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="value", + full_name="spotify.player.proto.ContextTrack.MetadataEntry.value", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=b"8\001", + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=169, + serialized_end=216, +) + +_CONTEXTTRACK = _descriptor.Descriptor( + name="ContextTrack", + full_name="spotify.player.proto.ContextTrack", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="uri", + full_name="spotify.player.proto.ContextTrack.uri", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="uid", + full_name="spotify.player.proto.ContextTrack.uid", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="gid", + full_name="spotify.player.proto.ContextTrack.gid", + index=2, + number=3, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="metadata", + full_name="spotify.player.proto.ContextTrack.metadata", + index=3, + number=4, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[ + _CONTEXTTRACK_METADATAENTRY, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=46, + serialized_end=216, +) + +_CONTEXTTRACK_METADATAENTRY.containing_type = _CONTEXTTRACK +_CONTEXTTRACK.fields_by_name[ + "metadata"].message_type = _CONTEXTTRACK_METADATAENTRY +DESCRIPTOR.message_types_by_name["ContextTrack"] = _CONTEXTTRACK +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ContextTrack = _reflection.GeneratedProtocolMessageType( + "ContextTrack", + (_message.Message, ), + { + "MetadataEntry": + _reflection.GeneratedProtocolMessageType( + "MetadataEntry", + (_message.Message, ), + { + "DESCRIPTOR": _CONTEXTTRACK_METADATAENTRY, + "__module__": "context_track_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.ContextTrack.MetadataEntry) + }, + ), + "DESCRIPTOR": + _CONTEXTTRACK, + "__module__": + "context_track_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.ContextTrack) + }, +) +_sym_db.RegisterMessage(ContextTrack) +_sym_db.RegisterMessage(ContextTrack.MetadataEntry) + +DESCRIPTOR._options = None +_CONTEXTTRACK_METADATAENTRY._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Context_pb2.py b/resources/lib/librespot/proto/Context_pb2.py new file mode 100644 index 0000000..0b2eeae --- /dev/null +++ b/resources/lib/librespot/proto/Context_pb2.py @@ -0,0 +1,260 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: context.proto +"""Generated protocol buffer code.""" +import ContextPage_pb2 as context__page__pb2 +import Restrictions_pb2 as restrictions__pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="context.proto", + package="spotify.player.proto", + syntax="proto2", + serialized_options=b"\n\023com.spotify.contextH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\rcontext.proto\x12\x14spotify.player.proto\x1a\x12\x63ontext_page.proto\x1a\x12restrictions.proto"\x90\x02\n\x07\x43ontext\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x0b\n\x03url\x18\x02 \x01(\t\x12=\n\x08metadata\x18\x03 \x03(\x0b\x32+.spotify.player.proto.Context.MetadataEntry\x12\x38\n\x0crestrictions\x18\x04 \x01(\x0b\x32".spotify.player.proto.Restrictions\x12\x30\n\x05pages\x18\x05 \x03(\x0b\x32!.spotify.player.proto.ContextPage\x12\x0f\n\x07loading\x18\x06 \x01(\x08\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x42\x17\n\x13\x63om.spotify.contextH\x02', + dependencies=[ + context__page__pb2.DESCRIPTOR, + restrictions__pb2.DESCRIPTOR, + ], +) + +_CONTEXT_METADATAENTRY = _descriptor.Descriptor( + name="MetadataEntry", + full_name="spotify.player.proto.Context.MetadataEntry", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="spotify.player.proto.Context.MetadataEntry.key", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="value", + full_name="spotify.player.proto.Context.MetadataEntry.value", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=b"8\001", + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=305, + serialized_end=352, +) + +_CONTEXT = _descriptor.Descriptor( + name="Context", + full_name="spotify.player.proto.Context", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="uri", + full_name="spotify.player.proto.Context.uri", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="url", + full_name="spotify.player.proto.Context.url", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="metadata", + full_name="spotify.player.proto.Context.metadata", + index=2, + number=3, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="restrictions", + full_name="spotify.player.proto.Context.restrictions", + index=3, + number=4, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="pages", + full_name="spotify.player.proto.Context.pages", + index=4, + number=5, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="loading", + full_name="spotify.player.proto.Context.loading", + index=5, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[ + _CONTEXT_METADATAENTRY, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=80, + serialized_end=352, +) + +_CONTEXT_METADATAENTRY.containing_type = _CONTEXT +_CONTEXT.fields_by_name["metadata"].message_type = _CONTEXT_METADATAENTRY +_CONTEXT.fields_by_name[ + "restrictions"].message_type = restrictions__pb2._RESTRICTIONS +_CONTEXT.fields_by_name["pages"].message_type = context__page__pb2._CONTEXTPAGE +DESCRIPTOR.message_types_by_name["Context"] = _CONTEXT +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Context = _reflection.GeneratedProtocolMessageType( + "Context", + (_message.Message, ), + { + "MetadataEntry": + _reflection.GeneratedProtocolMessageType( + "MetadataEntry", + (_message.Message, ), + { + "DESCRIPTOR": _CONTEXT_METADATAENTRY, + "__module__": "context_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.Context.MetadataEntry) + }, + ), + "DESCRIPTOR": + _CONTEXT, + "__module__": + "context_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.Context) + }, +) +_sym_db.RegisterMessage(Context) +_sym_db.RegisterMessage(Context.MetadataEntry) + +DESCRIPTOR._options = None +_CONTEXT_METADATAENTRY._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/ExplicitContentPubsub_pb2.py b/resources/lib/librespot/proto/ExplicitContentPubsub_pb2.py new file mode 100644 index 0000000..26ed33e --- /dev/null +++ b/resources/lib/librespot/proto/ExplicitContentPubsub_pb2.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: explicit_content_pubsub.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name='explicit_content_pubsub.proto', + package='spotify.explicit_content.proto', + syntax='proto2', + serialized_options=b'\n\024com.spotify.explicitH\002', + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x1d\x65xplicit_content_pubsub.proto\x12\x1espotify.explicit_content.proto\"*\n\x0cKeyValuePair\x12\x0b\n\x03key\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\t\"S\n\x14UserAttributesUpdate\x12;\n\x05pairs\x18\x01 \x03(\x0b\x32,.spotify.explicit_content.proto.KeyValuePairB\x18\n\x14\x63om.spotify.explicitH\x02' +) + +_KEYVALUEPAIR = _descriptor.Descriptor( + name='KeyValuePair', + full_name='spotify.explicit_content.proto.KeyValuePair', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', + full_name='spotify.explicit_content.proto.KeyValuePair.key', + index=0, + number=1, + type=9, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', + full_name='spotify.explicit_content.proto.KeyValuePair.value', + index=1, + number=2, + type=9, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=65, + serialized_end=107, +) + +_USERATTRIBUTESUPDATE = _descriptor.Descriptor( + name='UserAttributesUpdate', + full_name='spotify.explicit_content.proto.UserAttributesUpdate', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='pairs', + full_name= + 'spotify.explicit_content.proto.UserAttributesUpdate.pairs', + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=109, + serialized_end=192, +) + +_USERATTRIBUTESUPDATE.fields_by_name['pairs'].message_type = _KEYVALUEPAIR +DESCRIPTOR.message_types_by_name['KeyValuePair'] = _KEYVALUEPAIR +DESCRIPTOR.message_types_by_name[ + 'UserAttributesUpdate'] = _USERATTRIBUTESUPDATE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +KeyValuePair = _reflection.GeneratedProtocolMessageType( + 'KeyValuePair', + (_message.Message, ), + { + 'DESCRIPTOR': _KEYVALUEPAIR, + '__module__': 'explicit_content_pubsub_pb2' + # @@protoc_insertion_point(class_scope:spotify.explicit_content.proto.KeyValuePair) + }) +_sym_db.RegisterMessage(KeyValuePair) + +UserAttributesUpdate = _reflection.GeneratedProtocolMessageType( + 'UserAttributesUpdate', + (_message.Message, ), + { + 'DESCRIPTOR': _USERATTRIBUTESUPDATE, + '__module__': 'explicit_content_pubsub_pb2' + # @@protoc_insertion_point(class_scope:spotify.explicit_content.proto.UserAttributesUpdate) + }) +_sym_db.RegisterMessage(UserAttributesUpdate) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Keyexchange_pb2.py b/resources/lib/librespot/proto/Keyexchange_pb2.py new file mode 100644 index 0000000..663ee3b --- /dev/null +++ b/resources/lib/librespot/proto/Keyexchange_pb2.py @@ -0,0 +1,2497 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: keyexchange.proto +"""Generated protocol buffer code.""" +from google.protobuf.internal import enum_type_wrapper +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name='keyexchange.proto', + package='spotify', + syntax='proto2', + serialized_options=b'\n\013com.spotify', + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x11keyexchange.proto\x12\x07spotify\"\xe0\x02\n\x0b\x43lientHello\x12&\n\nbuild_info\x18\n \x02(\x0b\x32\x12.spotify.BuildInfo\x12\x34\n\x16\x66ingerprints_supported\x18\x14 \x03(\x0e\x32\x14.spotify.Fingerprint\x12\x34\n\x16\x63ryptosuites_supported\x18\x1e \x03(\x0e\x32\x14.spotify.Cryptosuite\x12\x30\n\x14powschemes_supported\x18( \x03(\x0e\x32\x12.spotify.Powscheme\x12:\n\x12login_crypto_hello\x18\x32 \x02(\x0b\x32\x1e.spotify.LoginCryptoHelloUnion\x12\x14\n\x0c\x63lient_nonce\x18< \x02(\x0c\x12\x0f\n\x07padding\x18\x46 \x01(\x0c\x12(\n\x0b\x66\x65\x61ture_set\x18P \x01(\x0b\x32\x13.spotify.FeatureSet\"\x92\x01\n\tBuildInfo\x12!\n\x07product\x18\n \x02(\x0e\x32\x10.spotify.Product\x12,\n\rproduct_flags\x18\x14 \x03(\x0e\x32\x15.spotify.ProductFlags\x12#\n\x08platform\x18\x1e \x02(\x0e\x32\x11.spotify.Platform\x12\x0f\n\x07version\x18( \x02(\x04\"W\n\x15LoginCryptoHelloUnion\x12>\n\x0e\x64iffie_hellman\x18\n \x01(\x0b\x32&.spotify.LoginCryptoDiffieHellmanHello\"F\n\x1dLoginCryptoDiffieHellmanHello\x12\n\n\x02gc\x18\n \x02(\x0c\x12\x19\n\x11server_keys_known\x18\x14 \x02(\r\";\n\nFeatureSet\x12\x13\n\x0b\x61utoupdate2\x18\x01 \x01(\x08\x12\x18\n\x10\x63urrent_location\x18\x02 \x01(\x08\"\x9c\x01\n\x11\x41PResponseMessage\x12\'\n\tchallenge\x18\n \x01(\x0b\x32\x14.spotify.APChallenge\x12\x30\n\x07upgrade\x18\x14 \x01(\x0b\x32\x1f.spotify.UpgradeRequiredMessage\x12,\n\x0clogin_failed\x18\x1e \x01(\x0b\x32\x16.spotify.APLoginFailed\"\xa7\x02\n\x0b\x41PChallenge\x12\x42\n\x16login_crypto_challenge\x18\n \x02(\x0b\x32\".spotify.LoginCryptoChallengeUnion\x12\x41\n\x15\x66ingerprint_challenge\x18\x14 \x02(\x0b\x32\".spotify.FingerprintChallengeUnion\x12\x31\n\rpow_challenge\x18\x1e \x02(\x0b\x32\x1a.spotify.PoWChallengeUnion\x12\x37\n\x10\x63rypto_challenge\x18( \x02(\x0b\x32\x1d.spotify.CryptoChallengeUnion\x12\x14\n\x0cserver_nonce\x18\x32 \x02(\x0c\x12\x0f\n\x07padding\x18< \x01(\x0c\"_\n\x19LoginCryptoChallengeUnion\x12\x42\n\x0e\x64iffie_hellman\x18\n \x01(\x0b\x32*.spotify.LoginCryptoDiffieHellmanChallenge\"c\n!LoginCryptoDiffieHellmanChallenge\x12\n\n\x02gs\x18\n \x02(\x0c\x12\x1c\n\x14server_signature_key\x18\x14 \x02(\x05\x12\x14\n\x0cgs_signature\x18\x1e \x02(\x0c\"\x8c\x01\n\x19\x46ingerprintChallengeUnion\x12\x31\n\x05grain\x18\n \x01(\x0b\x32\".spotify.FingerprintGrainChallenge\x12<\n\x0bhmac_ripemd\x18\x14 \x01(\x0b\x32\'.spotify.FingerprintHmacRipemdChallenge\"(\n\x19\x46ingerprintGrainChallenge\x12\x0b\n\x03kek\x18\n \x02(\x0c\"3\n\x1e\x46ingerprintHmacRipemdChallenge\x12\x11\n\tchallenge\x18\n \x02(\x0c\"E\n\x11PoWChallengeUnion\x12\x30\n\thash_cash\x18\n \x01(\x0b\x32\x1d.spotify.PoWHashCashChallenge\"F\n\x14PoWHashCashChallenge\x12\x0e\n\x06prefix\x18\n \x01(\x0c\x12\x0e\n\x06length\x18\x14 \x01(\x05\x12\x0e\n\x06target\x18\x1e \x01(\x05\"\x84\x01\n\x14\x43ryptoChallengeUnion\x12\x30\n\x07shannon\x18\n \x01(\x0b\x32\x1f.spotify.CryptoShannonChallenge\x12:\n\rrc4_sha1_hmac\x18\x14 \x01(\x0b\x32#.spotify.CryptoRc4Sha1HmacChallenge\"\x18\n\x16\x43ryptoShannonChallenge\"\x1c\n\x1a\x43ryptoRc4Sha1HmacChallenge\"]\n\x16UpgradeRequiredMessage\x12\x1b\n\x13upgrade_signed_part\x18\n \x02(\x0c\x12\x11\n\tsignature\x18\x14 \x02(\x0c\x12\x13\n\x0bhttp_suffix\x18\x1e \x01(\t\"w\n\rAPLoginFailed\x12&\n\nerror_code\x18\n \x02(\x0e\x32\x12.spotify.ErrorCode\x12\x13\n\x0bretry_delay\x18\x14 \x01(\x05\x12\x0e\n\x06\x65xpiry\x18\x1e \x01(\x05\x12\x19\n\x11\x65rror_description\x18( \x01(\t\"\xc3\x01\n\x17\x43lientResponsePlaintext\x12@\n\x15login_crypto_response\x18\n \x02(\x0b\x32!.spotify.LoginCryptoResponseUnion\x12/\n\x0cpow_response\x18\x14 \x02(\x0b\x32\x19.spotify.PoWResponseUnion\x12\x35\n\x0f\x63rypto_response\x18\x1e \x02(\x0b\x32\x1c.spotify.CryptoResponseUnion\"]\n\x18LoginCryptoResponseUnion\x12\x41\n\x0e\x64iffie_hellman\x18\n \x01(\x0b\x32).spotify.LoginCryptoDiffieHellmanResponse\"0\n LoginCryptoDiffieHellmanResponse\x12\x0c\n\x04hmac\x18\n \x02(\x0c\"C\n\x10PoWResponseUnion\x12/\n\thash_cash\x18\n \x01(\x0b\x32\x1c.spotify.PoWHashCashResponse\"*\n\x13PoWHashCashResponse\x12\x13\n\x0bhash_suffix\x18\n \x02(\x0c\"\x81\x01\n\x13\x43ryptoResponseUnion\x12/\n\x07shannon\x18\n \x01(\x0b\x32\x1e.spotify.CryptoShannonResponse\x12\x39\n\rrc4_sha1_hmac\x18\x14 \x01(\x0b\x32\".spotify.CryptoRc4Sha1HmacResponse\"&\n\x15\x43ryptoShannonResponse\x12\r\n\x05\x64ummy\x18\x01 \x01(\x05\"*\n\x19\x43ryptoRc4Sha1HmacResponse\x12\r\n\x05\x64ummy\x18\x01 \x01(\x05*\x7f\n\x07Product\x12\x12\n\x0ePRODUCT_CLIENT\x10\x00\x12\x16\n\x12PRODUCT_LIBSPOTIFY\x10\x01\x12\x12\n\x0ePRODUCT_MOBILE\x10\x02\x12\x13\n\x0fPRODUCT_PARTNER\x10\x03\x12\x1f\n\x1bPRODUCT_LIBSPOTIFY_EMBEDDED\x10\x05*A\n\x0cProductFlags\x12\x15\n\x11PRODUCT_FLAG_NONE\x10\x00\x12\x1a\n\x16PRODUCT_FLAG_DEV_BUILD\x10\x01*\xdc\x04\n\x08Platform\x12\x16\n\x12PLATFORM_WIN32_X86\x10\x00\x12\x14\n\x10PLATFORM_OSX_X86\x10\x01\x12\x16\n\x12PLATFORM_LINUX_X86\x10\x02\x12\x17\n\x13PLATFORM_IPHONE_ARM\x10\x03\x12\x14\n\x10PLATFORM_S60_ARM\x10\x04\x12\x14\n\x10PLATFORM_OSX_PPC\x10\x05\x12\x18\n\x14PLATFORM_ANDROID_ARM\x10\x06\x12\x1b\n\x17PLATFORM_WINDOWS_CE_ARM\x10\x07\x12\x19\n\x15PLATFORM_LINUX_X86_64\x10\x08\x12\x17\n\x13PLATFORM_OSX_X86_64\x10\t\x12\x15\n\x11PLATFORM_PALM_ARM\x10\n\x12\x15\n\x11PLATFORM_LINUX_SH\x10\x0b\x12\x18\n\x14PLATFORM_FREEBSD_X86\x10\x0c\x12\x1b\n\x17PLATFORM_FREEBSD_X86_64\x10\r\x12\x1b\n\x17PLATFORM_BLACKBERRY_ARM\x10\x0e\x12\x12\n\x0ePLATFORM_SONOS\x10\x0f\x12\x17\n\x13PLATFORM_LINUX_MIPS\x10\x10\x12\x16\n\x12PLATFORM_LINUX_ARM\x10\x11\x12\x19\n\x15PLATFORM_LOGITECH_ARM\x10\x12\x12\x1b\n\x17PLATFORM_LINUX_BLACKFIN\x10\x13\x12\x14\n\x10PLATFORM_WP7_ARM\x10\x14\x12\x16\n\x12PLATFORM_ONKYO_ARM\x10\x15\x12\x17\n\x13PLATFORM_QNXNTO_ARM\x10\x16\x12\x14\n\x10PLATFORM_BCO_ARM\x10\x17*A\n\x0b\x46ingerprint\x12\x15\n\x11\x46INGERPRINT_GRAIN\x10\x00\x12\x1b\n\x17\x46INGERPRINT_HMAC_RIPEMD\x10\x01*G\n\x0b\x43ryptosuite\x12\x18\n\x14\x43RYPTO_SUITE_SHANNON\x10\x00\x12\x1e\n\x1a\x43RYPTO_SUITE_RC4_SHA1_HMAC\x10\x01*\x1e\n\tPowscheme\x12\x11\n\rPOW_HASH_CASH\x10\x00*\x89\x02\n\tErrorCode\x12\x11\n\rProtocolError\x10\x00\x12\x10\n\x0cTryAnotherAP\x10\x02\x12\x13\n\x0f\x42\x61\x64\x43onnectionId\x10\x05\x12\x15\n\x11TravelRestriction\x10\t\x12\x1a\n\x16PremiumAccountRequired\x10\x0b\x12\x12\n\x0e\x42\x61\x64\x43redentials\x10\x0c\x12\x1f\n\x1b\x43ouldNotValidateCredentials\x10\r\x12\x11\n\rAccountExists\x10\x0e\x12\x1d\n\x19\x45xtraVerificationRequired\x10\x0f\x12\x11\n\rInvalidAppKey\x10\x10\x12\x15\n\x11\x41pplicationBanned\x10\x11\x42\r\n\x0b\x63om.spotify' +) + +_PRODUCT = _descriptor.EnumDescriptor( + name='Product', + full_name='spotify.Product', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='PRODUCT_CLIENT', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PRODUCT_LIBSPOTIFY', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PRODUCT_MOBILE', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PRODUCT_PARTNER', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PRODUCT_LIBSPOTIFY_EMBEDDED', + index=4, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=2871, + serialized_end=2998, +) +_sym_db.RegisterEnumDescriptor(_PRODUCT) + +Product = enum_type_wrapper.EnumTypeWrapper(_PRODUCT) +_PRODUCTFLAGS = _descriptor.EnumDescriptor( + name='ProductFlags', + full_name='spotify.ProductFlags', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='PRODUCT_FLAG_NONE', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PRODUCT_FLAG_DEV_BUILD', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3000, + serialized_end=3065, +) +_sym_db.RegisterEnumDescriptor(_PRODUCTFLAGS) + +ProductFlags = enum_type_wrapper.EnumTypeWrapper(_PRODUCTFLAGS) +_PLATFORM = _descriptor.EnumDescriptor( + name='Platform', + full_name='spotify.Platform', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='PLATFORM_WIN32_X86', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_OSX_X86', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_LINUX_X86', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_IPHONE_ARM', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_S60_ARM', + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_OSX_PPC', + index=5, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_ANDROID_ARM', + index=6, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_WINDOWS_CE_ARM', + index=7, + number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_LINUX_X86_64', + index=8, + number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_OSX_X86_64', + index=9, + number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_PALM_ARM', + index=10, + number=10, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_LINUX_SH', + index=11, + number=11, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_FREEBSD_X86', + index=12, + number=12, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_FREEBSD_X86_64', + index=13, + number=13, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_BLACKBERRY_ARM', + index=14, + number=14, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_SONOS', + index=15, + number=15, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_LINUX_MIPS', + index=16, + number=16, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_LINUX_ARM', + index=17, + number=17, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_LOGITECH_ARM', + index=18, + number=18, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_LINUX_BLACKFIN', + index=19, + number=19, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_WP7_ARM', + index=20, + number=20, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_ONKYO_ARM', + index=21, + number=21, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_QNXNTO_ARM', + index=22, + number=22, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PLATFORM_BCO_ARM', + index=23, + number=23, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3068, + serialized_end=3672, +) +_sym_db.RegisterEnumDescriptor(_PLATFORM) + +Platform = enum_type_wrapper.EnumTypeWrapper(_PLATFORM) +_FINGERPRINT = _descriptor.EnumDescriptor( + name='Fingerprint', + full_name='spotify.Fingerprint', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='FINGERPRINT_GRAIN', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='FINGERPRINT_HMAC_RIPEMD', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3674, + serialized_end=3739, +) +_sym_db.RegisterEnumDescriptor(_FINGERPRINT) + +Fingerprint = enum_type_wrapper.EnumTypeWrapper(_FINGERPRINT) +_CRYPTOSUITE = _descriptor.EnumDescriptor( + name='Cryptosuite', + full_name='spotify.Cryptosuite', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='CRYPTO_SUITE_SHANNON', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CRYPTO_SUITE_RC4_SHA1_HMAC', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3741, + serialized_end=3812, +) +_sym_db.RegisterEnumDescriptor(_CRYPTOSUITE) + +Cryptosuite = enum_type_wrapper.EnumTypeWrapper(_CRYPTOSUITE) +_POWSCHEME = _descriptor.EnumDescriptor( + name='Powscheme', + full_name='spotify.Powscheme', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='POW_HASH_CASH', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3814, + serialized_end=3844, +) +_sym_db.RegisterEnumDescriptor(_POWSCHEME) + +Powscheme = enum_type_wrapper.EnumTypeWrapper(_POWSCHEME) +_ERRORCODE = _descriptor.EnumDescriptor( + name='ErrorCode', + full_name='spotify.ErrorCode', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='ProtocolError', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TryAnotherAP', + index=1, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BadConnectionId', + index=2, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TravelRestriction', + index=3, + number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PremiumAccountRequired', + index=4, + number=11, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BadCredentials', + index=5, + number=12, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CouldNotValidateCredentials', + index=6, + number=13, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AccountExists', + index=7, + number=14, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ExtraVerificationRequired', + index=8, + number=15, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='InvalidAppKey', + index=9, + number=16, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='ApplicationBanned', + index=10, + number=17, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3847, + serialized_end=4112, +) +_sym_db.RegisterEnumDescriptor(_ERRORCODE) + +ErrorCode = enum_type_wrapper.EnumTypeWrapper(_ERRORCODE) +PRODUCT_CLIENT = 0 +PRODUCT_LIBSPOTIFY = 1 +PRODUCT_MOBILE = 2 +PRODUCT_PARTNER = 3 +PRODUCT_LIBSPOTIFY_EMBEDDED = 5 +PRODUCT_FLAG_NONE = 0 +PRODUCT_FLAG_DEV_BUILD = 1 +PLATFORM_WIN32_X86 = 0 +PLATFORM_OSX_X86 = 1 +PLATFORM_LINUX_X86 = 2 +PLATFORM_IPHONE_ARM = 3 +PLATFORM_S60_ARM = 4 +PLATFORM_OSX_PPC = 5 +PLATFORM_ANDROID_ARM = 6 +PLATFORM_WINDOWS_CE_ARM = 7 +PLATFORM_LINUX_X86_64 = 8 +PLATFORM_OSX_X86_64 = 9 +PLATFORM_PALM_ARM = 10 +PLATFORM_LINUX_SH = 11 +PLATFORM_FREEBSD_X86 = 12 +PLATFORM_FREEBSD_X86_64 = 13 +PLATFORM_BLACKBERRY_ARM = 14 +PLATFORM_SONOS = 15 +PLATFORM_LINUX_MIPS = 16 +PLATFORM_LINUX_ARM = 17 +PLATFORM_LOGITECH_ARM = 18 +PLATFORM_LINUX_BLACKFIN = 19 +PLATFORM_WP7_ARM = 20 +PLATFORM_ONKYO_ARM = 21 +PLATFORM_QNXNTO_ARM = 22 +PLATFORM_BCO_ARM = 23 +FINGERPRINT_GRAIN = 0 +FINGERPRINT_HMAC_RIPEMD = 1 +CRYPTO_SUITE_SHANNON = 0 +CRYPTO_SUITE_RC4_SHA1_HMAC = 1 +POW_HASH_CASH = 0 +ProtocolError = 0 +TryAnotherAP = 2 +BadConnectionId = 5 +TravelRestriction = 9 +PremiumAccountRequired = 11 +BadCredentials = 12 +CouldNotValidateCredentials = 13 +AccountExists = 14 +ExtraVerificationRequired = 15 +InvalidAppKey = 16 +ApplicationBanned = 17 + +_CLIENTHELLO = _descriptor.Descriptor( + name='ClientHello', + full_name='spotify.ClientHello', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='build_info', + full_name='spotify.ClientHello.build_info', + index=0, + number=10, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='fingerprints_supported', + full_name='spotify.ClientHello.fingerprints_supported', + index=1, + number=20, + type=14, + cpp_type=8, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cryptosuites_supported', + full_name='spotify.ClientHello.cryptosuites_supported', + index=2, + number=30, + type=14, + cpp_type=8, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='powschemes_supported', + full_name='spotify.ClientHello.powschemes_supported', + index=3, + number=40, + type=14, + cpp_type=8, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='login_crypto_hello', + full_name='spotify.ClientHello.login_crypto_hello', + index=4, + number=50, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='client_nonce', + full_name='spotify.ClientHello.client_nonce', + index=5, + number=60, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='padding', + full_name='spotify.ClientHello.padding', + index=6, + number=70, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='feature_set', + full_name='spotify.ClientHello.feature_set', + index=7, + number=80, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=31, + serialized_end=383, +) + +_BUILDINFO = _descriptor.Descriptor( + name='BuildInfo', + full_name='spotify.BuildInfo', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='product', + full_name='spotify.BuildInfo.product', + index=0, + number=10, + type=14, + cpp_type=8, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='product_flags', + full_name='spotify.BuildInfo.product_flags', + index=1, + number=20, + type=14, + cpp_type=8, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='platform', + full_name='spotify.BuildInfo.platform', + index=2, + number=30, + type=14, + cpp_type=8, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='version', + full_name='spotify.BuildInfo.version', + index=3, + number=40, + type=4, + cpp_type=4, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=386, + serialized_end=532, +) + +_LOGINCRYPTOHELLOUNION = _descriptor.Descriptor( + name='LoginCryptoHelloUnion', + full_name='spotify.LoginCryptoHelloUnion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='diffie_hellman', + full_name='spotify.LoginCryptoHelloUnion.diffie_hellman', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=534, + serialized_end=621, +) + +_LOGINCRYPTODIFFIEHELLMANHELLO = _descriptor.Descriptor( + name='LoginCryptoDiffieHellmanHello', + full_name='spotify.LoginCryptoDiffieHellmanHello', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='gc', + full_name='spotify.LoginCryptoDiffieHellmanHello.gc', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='server_keys_known', + full_name='spotify.LoginCryptoDiffieHellmanHello.server_keys_known', + index=1, + number=20, + type=13, + cpp_type=3, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=623, + serialized_end=693, +) + +_FEATURESET = _descriptor.Descriptor( + name='FeatureSet', + full_name='spotify.FeatureSet', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='autoupdate2', + full_name='spotify.FeatureSet.autoupdate2', + index=0, + number=1, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='current_location', + full_name='spotify.FeatureSet.current_location', + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=695, + serialized_end=754, +) + +_APRESPONSEMESSAGE = _descriptor.Descriptor( + name='APResponseMessage', + full_name='spotify.APResponseMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='challenge', + full_name='spotify.APResponseMessage.challenge', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='upgrade', + full_name='spotify.APResponseMessage.upgrade', + index=1, + number=20, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='login_failed', + full_name='spotify.APResponseMessage.login_failed', + index=2, + number=30, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=757, + serialized_end=913, +) + +_APCHALLENGE = _descriptor.Descriptor( + name='APChallenge', + full_name='spotify.APChallenge', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='login_crypto_challenge', + full_name='spotify.APChallenge.login_crypto_challenge', + index=0, + number=10, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='fingerprint_challenge', + full_name='spotify.APChallenge.fingerprint_challenge', + index=1, + number=20, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='pow_challenge', + full_name='spotify.APChallenge.pow_challenge', + index=2, + number=30, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='crypto_challenge', + full_name='spotify.APChallenge.crypto_challenge', + index=3, + number=40, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='server_nonce', + full_name='spotify.APChallenge.server_nonce', + index=4, + number=50, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='padding', + full_name='spotify.APChallenge.padding', + index=5, + number=60, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=916, + serialized_end=1211, +) + +_LOGINCRYPTOCHALLENGEUNION = _descriptor.Descriptor( + name='LoginCryptoChallengeUnion', + full_name='spotify.LoginCryptoChallengeUnion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='diffie_hellman', + full_name='spotify.LoginCryptoChallengeUnion.diffie_hellman', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1213, + serialized_end=1308, +) + +_LOGINCRYPTODIFFIEHELLMANCHALLENGE = _descriptor.Descriptor( + name='LoginCryptoDiffieHellmanChallenge', + full_name='spotify.LoginCryptoDiffieHellmanChallenge', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='gs', + full_name='spotify.LoginCryptoDiffieHellmanChallenge.gs', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='server_signature_key', + full_name= + 'spotify.LoginCryptoDiffieHellmanChallenge.server_signature_key', + index=1, + number=20, + type=5, + cpp_type=1, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='gs_signature', + full_name='spotify.LoginCryptoDiffieHellmanChallenge.gs_signature', + index=2, + number=30, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1310, + serialized_end=1409, +) + +_FINGERPRINTCHALLENGEUNION = _descriptor.Descriptor( + name='FingerprintChallengeUnion', + full_name='spotify.FingerprintChallengeUnion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='grain', + full_name='spotify.FingerprintChallengeUnion.grain', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='hmac_ripemd', + full_name='spotify.FingerprintChallengeUnion.hmac_ripemd', + index=1, + number=20, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1412, + serialized_end=1552, +) + +_FINGERPRINTGRAINCHALLENGE = _descriptor.Descriptor( + name='FingerprintGrainChallenge', + full_name='spotify.FingerprintGrainChallenge', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='kek', + full_name='spotify.FingerprintGrainChallenge.kek', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1554, + serialized_end=1594, +) + +_FINGERPRINTHMACRIPEMDCHALLENGE = _descriptor.Descriptor( + name='FingerprintHmacRipemdChallenge', + full_name='spotify.FingerprintHmacRipemdChallenge', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='challenge', + full_name='spotify.FingerprintHmacRipemdChallenge.challenge', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1596, + serialized_end=1647, +) + +_POWCHALLENGEUNION = _descriptor.Descriptor( + name='PoWChallengeUnion', + full_name='spotify.PoWChallengeUnion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='hash_cash', + full_name='spotify.PoWChallengeUnion.hash_cash', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1649, + serialized_end=1718, +) + +_POWHASHCASHCHALLENGE = _descriptor.Descriptor( + name='PoWHashCashChallenge', + full_name='spotify.PoWHashCashChallenge', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='prefix', + full_name='spotify.PoWHashCashChallenge.prefix', + index=0, + number=10, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='length', + full_name='spotify.PoWHashCashChallenge.length', + index=1, + number=20, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='target', + full_name='spotify.PoWHashCashChallenge.target', + index=2, + number=30, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1720, + serialized_end=1790, +) + +_CRYPTOCHALLENGEUNION = _descriptor.Descriptor( + name='CryptoChallengeUnion', + full_name='spotify.CryptoChallengeUnion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='shannon', + full_name='spotify.CryptoChallengeUnion.shannon', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='rc4_sha1_hmac', + full_name='spotify.CryptoChallengeUnion.rc4_sha1_hmac', + index=1, + number=20, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1793, + serialized_end=1925, +) + +_CRYPTOSHANNONCHALLENGE = _descriptor.Descriptor( + name='CryptoShannonChallenge', + full_name='spotify.CryptoShannonChallenge', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1927, + serialized_end=1951, +) + +_CRYPTORC4SHA1HMACCHALLENGE = _descriptor.Descriptor( + name='CryptoRc4Sha1HmacChallenge', + full_name='spotify.CryptoRc4Sha1HmacChallenge', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1953, + serialized_end=1981, +) + +_UPGRADEREQUIREDMESSAGE = _descriptor.Descriptor( + name='UpgradeRequiredMessage', + full_name='spotify.UpgradeRequiredMessage', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='upgrade_signed_part', + full_name='spotify.UpgradeRequiredMessage.upgrade_signed_part', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='signature', + full_name='spotify.UpgradeRequiredMessage.signature', + index=1, + number=20, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='http_suffix', + full_name='spotify.UpgradeRequiredMessage.http_suffix', + index=2, + number=30, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1983, + serialized_end=2076, +) + +_APLOGINFAILED = _descriptor.Descriptor( + name='APLoginFailed', + full_name='spotify.APLoginFailed', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='error_code', + full_name='spotify.APLoginFailed.error_code', + index=0, + number=10, + type=14, + cpp_type=8, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='retry_delay', + full_name='spotify.APLoginFailed.retry_delay', + index=1, + number=20, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='expiry', + full_name='spotify.APLoginFailed.expiry', + index=2, + number=30, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='error_description', + full_name='spotify.APLoginFailed.error_description', + index=3, + number=40, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2078, + serialized_end=2197, +) + +_CLIENTRESPONSEPLAINTEXT = _descriptor.Descriptor( + name='ClientResponsePlaintext', + full_name='spotify.ClientResponsePlaintext', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='login_crypto_response', + full_name='spotify.ClientResponsePlaintext.login_crypto_response', + index=0, + number=10, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='pow_response', + full_name='spotify.ClientResponsePlaintext.pow_response', + index=1, + number=20, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='crypto_response', + full_name='spotify.ClientResponsePlaintext.crypto_response', + index=2, + number=30, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2200, + serialized_end=2395, +) + +_LOGINCRYPTORESPONSEUNION = _descriptor.Descriptor( + name='LoginCryptoResponseUnion', + full_name='spotify.LoginCryptoResponseUnion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='diffie_hellman', + full_name='spotify.LoginCryptoResponseUnion.diffie_hellman', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2397, + serialized_end=2490, +) + +_LOGINCRYPTODIFFIEHELLMANRESPONSE = _descriptor.Descriptor( + name='LoginCryptoDiffieHellmanResponse', + full_name='spotify.LoginCryptoDiffieHellmanResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='hmac', + full_name='spotify.LoginCryptoDiffieHellmanResponse.hmac', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2492, + serialized_end=2540, +) + +_POWRESPONSEUNION = _descriptor.Descriptor( + name='PoWResponseUnion', + full_name='spotify.PoWResponseUnion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='hash_cash', + full_name='spotify.PoWResponseUnion.hash_cash', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2542, + serialized_end=2609, +) + +_POWHASHCASHRESPONSE = _descriptor.Descriptor( + name='PoWHashCashResponse', + full_name='spotify.PoWHashCashResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='hash_suffix', + full_name='spotify.PoWHashCashResponse.hash_suffix', + index=0, + number=10, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2611, + serialized_end=2653, +) + +_CRYPTORESPONSEUNION = _descriptor.Descriptor( + name='CryptoResponseUnion', + full_name='spotify.CryptoResponseUnion', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='shannon', + full_name='spotify.CryptoResponseUnion.shannon', + index=0, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='rc4_sha1_hmac', + full_name='spotify.CryptoResponseUnion.rc4_sha1_hmac', + index=1, + number=20, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2656, + serialized_end=2785, +) + +_CRYPTOSHANNONRESPONSE = _descriptor.Descriptor( + name='CryptoShannonResponse', + full_name='spotify.CryptoShannonResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='dummy', + full_name='spotify.CryptoShannonResponse.dummy', + index=0, + number=1, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2787, + serialized_end=2825, +) + +_CRYPTORC4SHA1HMACRESPONSE = _descriptor.Descriptor( + name='CryptoRc4Sha1HmacResponse', + full_name='spotify.CryptoRc4Sha1HmacResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='dummy', + full_name='spotify.CryptoRc4Sha1HmacResponse.dummy', + index=0, + number=1, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2827, + serialized_end=2869, +) + +_CLIENTHELLO.fields_by_name['build_info'].message_type = _BUILDINFO +_CLIENTHELLO.fields_by_name['fingerprints_supported'].enum_type = _FINGERPRINT +_CLIENTHELLO.fields_by_name['cryptosuites_supported'].enum_type = _CRYPTOSUITE +_CLIENTHELLO.fields_by_name['powschemes_supported'].enum_type = _POWSCHEME +_CLIENTHELLO.fields_by_name[ + 'login_crypto_hello'].message_type = _LOGINCRYPTOHELLOUNION +_CLIENTHELLO.fields_by_name['feature_set'].message_type = _FEATURESET +_BUILDINFO.fields_by_name['product'].enum_type = _PRODUCT +_BUILDINFO.fields_by_name['product_flags'].enum_type = _PRODUCTFLAGS +_BUILDINFO.fields_by_name['platform'].enum_type = _PLATFORM +_LOGINCRYPTOHELLOUNION.fields_by_name[ + 'diffie_hellman'].message_type = _LOGINCRYPTODIFFIEHELLMANHELLO +_APRESPONSEMESSAGE.fields_by_name['challenge'].message_type = _APCHALLENGE +_APRESPONSEMESSAGE.fields_by_name[ + 'upgrade'].message_type = _UPGRADEREQUIREDMESSAGE +_APRESPONSEMESSAGE.fields_by_name['login_failed'].message_type = _APLOGINFAILED +_APCHALLENGE.fields_by_name[ + 'login_crypto_challenge'].message_type = _LOGINCRYPTOCHALLENGEUNION +_APCHALLENGE.fields_by_name[ + 'fingerprint_challenge'].message_type = _FINGERPRINTCHALLENGEUNION +_APCHALLENGE.fields_by_name['pow_challenge'].message_type = _POWCHALLENGEUNION +_APCHALLENGE.fields_by_name[ + 'crypto_challenge'].message_type = _CRYPTOCHALLENGEUNION +_LOGINCRYPTOCHALLENGEUNION.fields_by_name[ + 'diffie_hellman'].message_type = _LOGINCRYPTODIFFIEHELLMANCHALLENGE +_FINGERPRINTCHALLENGEUNION.fields_by_name[ + 'grain'].message_type = _FINGERPRINTGRAINCHALLENGE +_FINGERPRINTCHALLENGEUNION.fields_by_name[ + 'hmac_ripemd'].message_type = _FINGERPRINTHMACRIPEMDCHALLENGE +_POWCHALLENGEUNION.fields_by_name[ + 'hash_cash'].message_type = _POWHASHCASHCHALLENGE +_CRYPTOCHALLENGEUNION.fields_by_name[ + 'shannon'].message_type = _CRYPTOSHANNONCHALLENGE +_CRYPTOCHALLENGEUNION.fields_by_name[ + 'rc4_sha1_hmac'].message_type = _CRYPTORC4SHA1HMACCHALLENGE +_APLOGINFAILED.fields_by_name['error_code'].enum_type = _ERRORCODE +_CLIENTRESPONSEPLAINTEXT.fields_by_name[ + 'login_crypto_response'].message_type = _LOGINCRYPTORESPONSEUNION +_CLIENTRESPONSEPLAINTEXT.fields_by_name[ + 'pow_response'].message_type = _POWRESPONSEUNION +_CLIENTRESPONSEPLAINTEXT.fields_by_name[ + 'crypto_response'].message_type = _CRYPTORESPONSEUNION +_LOGINCRYPTORESPONSEUNION.fields_by_name[ + 'diffie_hellman'].message_type = _LOGINCRYPTODIFFIEHELLMANRESPONSE +_POWRESPONSEUNION.fields_by_name[ + 'hash_cash'].message_type = _POWHASHCASHRESPONSE +_CRYPTORESPONSEUNION.fields_by_name[ + 'shannon'].message_type = _CRYPTOSHANNONRESPONSE +_CRYPTORESPONSEUNION.fields_by_name[ + 'rc4_sha1_hmac'].message_type = _CRYPTORC4SHA1HMACRESPONSE +DESCRIPTOR.message_types_by_name['ClientHello'] = _CLIENTHELLO +DESCRIPTOR.message_types_by_name['BuildInfo'] = _BUILDINFO +DESCRIPTOR.message_types_by_name[ + 'LoginCryptoHelloUnion'] = _LOGINCRYPTOHELLOUNION +DESCRIPTOR.message_types_by_name[ + 'LoginCryptoDiffieHellmanHello'] = _LOGINCRYPTODIFFIEHELLMANHELLO +DESCRIPTOR.message_types_by_name['FeatureSet'] = _FEATURESET +DESCRIPTOR.message_types_by_name['APResponseMessage'] = _APRESPONSEMESSAGE +DESCRIPTOR.message_types_by_name['APChallenge'] = _APCHALLENGE +DESCRIPTOR.message_types_by_name[ + 'LoginCryptoChallengeUnion'] = _LOGINCRYPTOCHALLENGEUNION +DESCRIPTOR.message_types_by_name[ + 'LoginCryptoDiffieHellmanChallenge'] = _LOGINCRYPTODIFFIEHELLMANCHALLENGE +DESCRIPTOR.message_types_by_name[ + 'FingerprintChallengeUnion'] = _FINGERPRINTCHALLENGEUNION +DESCRIPTOR.message_types_by_name[ + 'FingerprintGrainChallenge'] = _FINGERPRINTGRAINCHALLENGE +DESCRIPTOR.message_types_by_name[ + 'FingerprintHmacRipemdChallenge'] = _FINGERPRINTHMACRIPEMDCHALLENGE +DESCRIPTOR.message_types_by_name['PoWChallengeUnion'] = _POWCHALLENGEUNION +DESCRIPTOR.message_types_by_name[ + 'PoWHashCashChallenge'] = _POWHASHCASHCHALLENGE +DESCRIPTOR.message_types_by_name[ + 'CryptoChallengeUnion'] = _CRYPTOCHALLENGEUNION +DESCRIPTOR.message_types_by_name[ + 'CryptoShannonChallenge'] = _CRYPTOSHANNONCHALLENGE +DESCRIPTOR.message_types_by_name[ + 'CryptoRc4Sha1HmacChallenge'] = _CRYPTORC4SHA1HMACCHALLENGE +DESCRIPTOR.message_types_by_name[ + 'UpgradeRequiredMessage'] = _UPGRADEREQUIREDMESSAGE +DESCRIPTOR.message_types_by_name['APLoginFailed'] = _APLOGINFAILED +DESCRIPTOR.message_types_by_name[ + 'ClientResponsePlaintext'] = _CLIENTRESPONSEPLAINTEXT +DESCRIPTOR.message_types_by_name[ + 'LoginCryptoResponseUnion'] = _LOGINCRYPTORESPONSEUNION +DESCRIPTOR.message_types_by_name[ + 'LoginCryptoDiffieHellmanResponse'] = _LOGINCRYPTODIFFIEHELLMANRESPONSE +DESCRIPTOR.message_types_by_name['PoWResponseUnion'] = _POWRESPONSEUNION +DESCRIPTOR.message_types_by_name['PoWHashCashResponse'] = _POWHASHCASHRESPONSE +DESCRIPTOR.message_types_by_name['CryptoResponseUnion'] = _CRYPTORESPONSEUNION +DESCRIPTOR.message_types_by_name[ + 'CryptoShannonResponse'] = _CRYPTOSHANNONRESPONSE +DESCRIPTOR.message_types_by_name[ + 'CryptoRc4Sha1HmacResponse'] = _CRYPTORC4SHA1HMACRESPONSE +DESCRIPTOR.enum_types_by_name['Product'] = _PRODUCT +DESCRIPTOR.enum_types_by_name['ProductFlags'] = _PRODUCTFLAGS +DESCRIPTOR.enum_types_by_name['Platform'] = _PLATFORM +DESCRIPTOR.enum_types_by_name['Fingerprint'] = _FINGERPRINT +DESCRIPTOR.enum_types_by_name['Cryptosuite'] = _CRYPTOSUITE +DESCRIPTOR.enum_types_by_name['Powscheme'] = _POWSCHEME +DESCRIPTOR.enum_types_by_name['ErrorCode'] = _ERRORCODE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ClientHello = _reflection.GeneratedProtocolMessageType( + 'ClientHello', + (_message.Message, ), + { + 'DESCRIPTOR': _CLIENTHELLO, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.ClientHello) + }) +_sym_db.RegisterMessage(ClientHello) + +BuildInfo = _reflection.GeneratedProtocolMessageType( + 'BuildInfo', + (_message.Message, ), + { + 'DESCRIPTOR': _BUILDINFO, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.BuildInfo) + }) +_sym_db.RegisterMessage(BuildInfo) + +LoginCryptoHelloUnion = _reflection.GeneratedProtocolMessageType( + 'LoginCryptoHelloUnion', + (_message.Message, ), + { + 'DESCRIPTOR': _LOGINCRYPTOHELLOUNION, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.LoginCryptoHelloUnion) + }) +_sym_db.RegisterMessage(LoginCryptoHelloUnion) + +LoginCryptoDiffieHellmanHello = _reflection.GeneratedProtocolMessageType( + 'LoginCryptoDiffieHellmanHello', + (_message.Message, ), + { + 'DESCRIPTOR': _LOGINCRYPTODIFFIEHELLMANHELLO, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.LoginCryptoDiffieHellmanHello) + }) +_sym_db.RegisterMessage(LoginCryptoDiffieHellmanHello) + +FeatureSet = _reflection.GeneratedProtocolMessageType( + 'FeatureSet', + (_message.Message, ), + { + 'DESCRIPTOR': _FEATURESET, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.FeatureSet) + }) +_sym_db.RegisterMessage(FeatureSet) + +APResponseMessage = _reflection.GeneratedProtocolMessageType( + 'APResponseMessage', + (_message.Message, ), + { + 'DESCRIPTOR': _APRESPONSEMESSAGE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.APResponseMessage) + }) +_sym_db.RegisterMessage(APResponseMessage) + +APChallenge = _reflection.GeneratedProtocolMessageType( + 'APChallenge', + (_message.Message, ), + { + 'DESCRIPTOR': _APCHALLENGE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.APChallenge) + }) +_sym_db.RegisterMessage(APChallenge) + +LoginCryptoChallengeUnion = _reflection.GeneratedProtocolMessageType( + 'LoginCryptoChallengeUnion', + (_message.Message, ), + { + 'DESCRIPTOR': _LOGINCRYPTOCHALLENGEUNION, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.LoginCryptoChallengeUnion) + }) +_sym_db.RegisterMessage(LoginCryptoChallengeUnion) + +LoginCryptoDiffieHellmanChallenge = _reflection.GeneratedProtocolMessageType( + 'LoginCryptoDiffieHellmanChallenge', + (_message.Message, ), + { + 'DESCRIPTOR': _LOGINCRYPTODIFFIEHELLMANCHALLENGE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.LoginCryptoDiffieHellmanChallenge) + }) +_sym_db.RegisterMessage(LoginCryptoDiffieHellmanChallenge) + +FingerprintChallengeUnion = _reflection.GeneratedProtocolMessageType( + 'FingerprintChallengeUnion', + (_message.Message, ), + { + 'DESCRIPTOR': _FINGERPRINTCHALLENGEUNION, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.FingerprintChallengeUnion) + }) +_sym_db.RegisterMessage(FingerprintChallengeUnion) + +FingerprintGrainChallenge = _reflection.GeneratedProtocolMessageType( + 'FingerprintGrainChallenge', + (_message.Message, ), + { + 'DESCRIPTOR': _FINGERPRINTGRAINCHALLENGE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.FingerprintGrainChallenge) + }) +_sym_db.RegisterMessage(FingerprintGrainChallenge) + +FingerprintHmacRipemdChallenge = _reflection.GeneratedProtocolMessageType( + 'FingerprintHmacRipemdChallenge', + (_message.Message, ), + { + 'DESCRIPTOR': _FINGERPRINTHMACRIPEMDCHALLENGE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.FingerprintHmacRipemdChallenge) + }) +_sym_db.RegisterMessage(FingerprintHmacRipemdChallenge) + +PoWChallengeUnion = _reflection.GeneratedProtocolMessageType( + 'PoWChallengeUnion', + (_message.Message, ), + { + 'DESCRIPTOR': _POWCHALLENGEUNION, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.PoWChallengeUnion) + }) +_sym_db.RegisterMessage(PoWChallengeUnion) + +PoWHashCashChallenge = _reflection.GeneratedProtocolMessageType( + 'PoWHashCashChallenge', + (_message.Message, ), + { + 'DESCRIPTOR': _POWHASHCASHCHALLENGE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.PoWHashCashChallenge) + }) +_sym_db.RegisterMessage(PoWHashCashChallenge) + +CryptoChallengeUnion = _reflection.GeneratedProtocolMessageType( + 'CryptoChallengeUnion', + (_message.Message, ), + { + 'DESCRIPTOR': _CRYPTOCHALLENGEUNION, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.CryptoChallengeUnion) + }) +_sym_db.RegisterMessage(CryptoChallengeUnion) + +CryptoShannonChallenge = _reflection.GeneratedProtocolMessageType( + 'CryptoShannonChallenge', + (_message.Message, ), + { + 'DESCRIPTOR': _CRYPTOSHANNONCHALLENGE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.CryptoShannonChallenge) + }) +_sym_db.RegisterMessage(CryptoShannonChallenge) + +CryptoRc4Sha1HmacChallenge = _reflection.GeneratedProtocolMessageType( + 'CryptoRc4Sha1HmacChallenge', + (_message.Message, ), + { + 'DESCRIPTOR': _CRYPTORC4SHA1HMACCHALLENGE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.CryptoRc4Sha1HmacChallenge) + }) +_sym_db.RegisterMessage(CryptoRc4Sha1HmacChallenge) + +UpgradeRequiredMessage = _reflection.GeneratedProtocolMessageType( + 'UpgradeRequiredMessage', + (_message.Message, ), + { + 'DESCRIPTOR': _UPGRADEREQUIREDMESSAGE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.UpgradeRequiredMessage) + }) +_sym_db.RegisterMessage(UpgradeRequiredMessage) + +APLoginFailed = _reflection.GeneratedProtocolMessageType( + 'APLoginFailed', + (_message.Message, ), + { + 'DESCRIPTOR': _APLOGINFAILED, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.APLoginFailed) + }) +_sym_db.RegisterMessage(APLoginFailed) + +ClientResponsePlaintext = _reflection.GeneratedProtocolMessageType( + 'ClientResponsePlaintext', + (_message.Message, ), + { + 'DESCRIPTOR': _CLIENTRESPONSEPLAINTEXT, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.ClientResponsePlaintext) + }) +_sym_db.RegisterMessage(ClientResponsePlaintext) + +LoginCryptoResponseUnion = _reflection.GeneratedProtocolMessageType( + 'LoginCryptoResponseUnion', + (_message.Message, ), + { + 'DESCRIPTOR': _LOGINCRYPTORESPONSEUNION, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.LoginCryptoResponseUnion) + }) +_sym_db.RegisterMessage(LoginCryptoResponseUnion) + +LoginCryptoDiffieHellmanResponse = _reflection.GeneratedProtocolMessageType( + 'LoginCryptoDiffieHellmanResponse', + (_message.Message, ), + { + 'DESCRIPTOR': _LOGINCRYPTODIFFIEHELLMANRESPONSE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.LoginCryptoDiffieHellmanResponse) + }) +_sym_db.RegisterMessage(LoginCryptoDiffieHellmanResponse) + +PoWResponseUnion = _reflection.GeneratedProtocolMessageType( + 'PoWResponseUnion', + (_message.Message, ), + { + 'DESCRIPTOR': _POWRESPONSEUNION, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.PoWResponseUnion) + }) +_sym_db.RegisterMessage(PoWResponseUnion) + +PoWHashCashResponse = _reflection.GeneratedProtocolMessageType( + 'PoWHashCashResponse', + (_message.Message, ), + { + 'DESCRIPTOR': _POWHASHCASHRESPONSE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.PoWHashCashResponse) + }) +_sym_db.RegisterMessage(PoWHashCashResponse) + +CryptoResponseUnion = _reflection.GeneratedProtocolMessageType( + 'CryptoResponseUnion', + (_message.Message, ), + { + 'DESCRIPTOR': _CRYPTORESPONSEUNION, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.CryptoResponseUnion) + }) +_sym_db.RegisterMessage(CryptoResponseUnion) + +CryptoShannonResponse = _reflection.GeneratedProtocolMessageType( + 'CryptoShannonResponse', + (_message.Message, ), + { + 'DESCRIPTOR': _CRYPTOSHANNONRESPONSE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.CryptoShannonResponse) + }) +_sym_db.RegisterMessage(CryptoShannonResponse) + +CryptoRc4Sha1HmacResponse = _reflection.GeneratedProtocolMessageType( + 'CryptoRc4Sha1HmacResponse', + (_message.Message, ), + { + 'DESCRIPTOR': _CRYPTORC4SHA1HMACRESPONSE, + '__module__': 'keyexchange_pb2' + # @@protoc_insertion_point(class_scope:spotify.CryptoRc4Sha1HmacResponse) + }) +_sym_db.RegisterMessage(CryptoRc4Sha1HmacResponse) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Mercury_pb2.py b/resources/lib/librespot/proto/Mercury_pb2.py new file mode 100644 index 0000000..8109c9f --- /dev/null +++ b/resources/lib/librespot/proto/Mercury_pb2.py @@ -0,0 +1,625 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: mercury.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name='mercury.proto', + package='spotify', + syntax='proto2', + serialized_options=b'\n\013com.spotify', + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\rmercury.proto\x12\x07spotify\"B\n\x16MercuryMultiGetRequest\x12(\n\x07request\x18\x01 \x03(\x0b\x32\x17.spotify.MercuryRequest\"<\n\x14MercuryMultiGetReply\x12$\n\x05reply\x18\x01 \x03(\x0b\x32\x15.spotify.MercuryReply\"O\n\x0eMercuryRequest\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\x12\x0c\n\x04\x62ody\x18\x03 \x01(\x0c\x12\x0c\n\x04\x65tag\x18\x04 \x01(\x0c\"\xf5\x01\n\x0cMercuryReply\x12\x13\n\x0bstatus_code\x18\x01 \x01(\x11\x12\x16\n\x0estatus_message\x18\x02 \x01(\t\x12\x37\n\x0c\x63\x61\x63he_policy\x18\x03 \x01(\x0e\x32!.spotify.MercuryReply.CachePolicy\x12\x0b\n\x03ttl\x18\x04 \x01(\x11\x12\x0c\n\x04\x65tag\x18\x05 \x01(\x0c\x12\x14\n\x0c\x63ontent_type\x18\x06 \x01(\t\x12\x0c\n\x04\x62ody\x18\x07 \x01(\x0c\"@\n\x0b\x43\x61\x63hePolicy\x12\x0c\n\x08\x43\x41\x43HE_NO\x10\x01\x12\x11\n\rCACHE_PRIVATE\x10\x02\x12\x10\n\x0c\x43\x41\x43HE_PUBLIC\x10\x03\"y\n\x06Header\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x14\n\x0c\x63ontent_type\x18\x02 \x01(\t\x12\x0e\n\x06method\x18\x03 \x01(\t\x12\x13\n\x0bstatus_code\x18\x04 \x01(\x11\x12\'\n\x0buser_fields\x18\x06 \x03(\x0b\x32\x12.spotify.UserField\"\'\n\tUserField\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\x0c\x42\r\n\x0b\x63om.spotify' +) + +_MERCURYREPLY_CACHEPOLICY = _descriptor.EnumDescriptor( + name='CachePolicy', + full_name='spotify.MercuryReply.CachePolicy', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='CACHE_NO', + index=0, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CACHE_PRIVATE', + index=1, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CACHE_PUBLIC', + index=2, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=419, + serialized_end=483, +) +_sym_db.RegisterEnumDescriptor(_MERCURYREPLY_CACHEPOLICY) + +_MERCURYMULTIGETREQUEST = _descriptor.Descriptor( + name='MercuryMultiGetRequest', + full_name='spotify.MercuryMultiGetRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='request', + full_name='spotify.MercuryMultiGetRequest.request', + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=26, + serialized_end=92, +) + +_MERCURYMULTIGETREPLY = _descriptor.Descriptor( + name='MercuryMultiGetReply', + full_name='spotify.MercuryMultiGetReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='reply', + full_name='spotify.MercuryMultiGetReply.reply', + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=94, + serialized_end=154, +) + +_MERCURYREQUEST = _descriptor.Descriptor( + name='MercuryRequest', + full_name='spotify.MercuryRequest', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uri', + full_name='spotify.MercuryRequest.uri', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='content_type', + full_name='spotify.MercuryRequest.content_type', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='body', + full_name='spotify.MercuryRequest.body', + index=2, + number=3, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='etag', + full_name='spotify.MercuryRequest.etag', + index=3, + number=4, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=156, + serialized_end=235, +) + +_MERCURYREPLY = _descriptor.Descriptor( + name='MercuryReply', + full_name='spotify.MercuryReply', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='status_code', + full_name='spotify.MercuryReply.status_code', + index=0, + number=1, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='status_message', + full_name='spotify.MercuryReply.status_message', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cache_policy', + full_name='spotify.MercuryReply.cache_policy', + index=2, + number=3, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=1, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='ttl', + full_name='spotify.MercuryReply.ttl', + index=3, + number=4, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='etag', + full_name='spotify.MercuryReply.etag', + index=4, + number=5, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='content_type', + full_name='spotify.MercuryReply.content_type', + index=5, + number=6, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='body', + full_name='spotify.MercuryReply.body', + index=6, + number=7, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[ + _MERCURYREPLY_CACHEPOLICY, + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=238, + serialized_end=483, +) + +_HEADER = _descriptor.Descriptor( + name='Header', + full_name='spotify.Header', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uri', + full_name='spotify.Header.uri', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='content_type', + full_name='spotify.Header.content_type', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='method', + full_name='spotify.Header.method', + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='status_code', + full_name='spotify.Header.status_code', + index=3, + number=4, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='user_fields', + full_name='spotify.Header.user_fields', + index=4, + number=6, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=485, + serialized_end=606, +) + +_USERFIELD = _descriptor.Descriptor( + name='UserField', + full_name='spotify.UserField', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', + full_name='spotify.UserField.key', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', + full_name='spotify.UserField.value', + index=1, + number=2, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=608, + serialized_end=647, +) + +_MERCURYMULTIGETREQUEST.fields_by_name[ + 'request'].message_type = _MERCURYREQUEST +_MERCURYMULTIGETREPLY.fields_by_name['reply'].message_type = _MERCURYREPLY +_MERCURYREPLY.fields_by_name[ + 'cache_policy'].enum_type = _MERCURYREPLY_CACHEPOLICY +_MERCURYREPLY_CACHEPOLICY.containing_type = _MERCURYREPLY +_HEADER.fields_by_name['user_fields'].message_type = _USERFIELD +DESCRIPTOR.message_types_by_name[ + 'MercuryMultiGetRequest'] = _MERCURYMULTIGETREQUEST +DESCRIPTOR.message_types_by_name[ + 'MercuryMultiGetReply'] = _MERCURYMULTIGETREPLY +DESCRIPTOR.message_types_by_name['MercuryRequest'] = _MERCURYREQUEST +DESCRIPTOR.message_types_by_name['MercuryReply'] = _MERCURYREPLY +DESCRIPTOR.message_types_by_name['Header'] = _HEADER +DESCRIPTOR.message_types_by_name['UserField'] = _USERFIELD +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +MercuryMultiGetRequest = _reflection.GeneratedProtocolMessageType( + 'MercuryMultiGetRequest', + (_message.Message, ), + { + 'DESCRIPTOR': _MERCURYMULTIGETREQUEST, + '__module__': 'mercury_pb2' + # @@protoc_insertion_point(class_scope:spotify.MercuryMultiGetRequest) + }) +_sym_db.RegisterMessage(MercuryMultiGetRequest) + +MercuryMultiGetReply = _reflection.GeneratedProtocolMessageType( + 'MercuryMultiGetReply', + (_message.Message, ), + { + 'DESCRIPTOR': _MERCURYMULTIGETREPLY, + '__module__': 'mercury_pb2' + # @@protoc_insertion_point(class_scope:spotify.MercuryMultiGetReply) + }) +_sym_db.RegisterMessage(MercuryMultiGetReply) + +MercuryRequest = _reflection.GeneratedProtocolMessageType( + 'MercuryRequest', + (_message.Message, ), + { + 'DESCRIPTOR': _MERCURYREQUEST, + '__module__': 'mercury_pb2' + # @@protoc_insertion_point(class_scope:spotify.MercuryRequest) + }) +_sym_db.RegisterMessage(MercuryRequest) + +MercuryReply = _reflection.GeneratedProtocolMessageType( + 'MercuryReply', + (_message.Message, ), + { + 'DESCRIPTOR': _MERCURYREPLY, + '__module__': 'mercury_pb2' + # @@protoc_insertion_point(class_scope:spotify.MercuryReply) + }) +_sym_db.RegisterMessage(MercuryReply) + +Header = _reflection.GeneratedProtocolMessageType( + 'Header', + (_message.Message, ), + { + 'DESCRIPTOR': _HEADER, + '__module__': 'mercury_pb2' + # @@protoc_insertion_point(class_scope:spotify.Header) + }) +_sym_db.RegisterMessage(Header) + +UserField = _reflection.GeneratedProtocolMessageType( + 'UserField', + (_message.Message, ), + { + 'DESCRIPTOR': _USERFIELD, + '__module__': 'mercury_pb2' + # @@protoc_insertion_point(class_scope:spotify.UserField) + }) +_sym_db.RegisterMessage(UserField) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Metadata_pb2.py b/resources/lib/librespot/proto/Metadata_pb2.py new file mode 100644 index 0000000..e8ac38c --- /dev/null +++ b/resources/lib/librespot/proto/Metadata_pb2.py @@ -0,0 +1,3698 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: metadata.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name='metadata.proto', + package='spotify.metadata.proto', + syntax='proto2', + serialized_options=b'\n\024com.spotify.metadataB\010MetadataH\002', + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x0emetadata.proto\x12\x16spotify.metadata.proto\"\x8a\x07\n\x06\x41rtist\x12\x0b\n\x03gid\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x12\n\npopularity\x18\x03 \x01(\x11\x12\x34\n\ttop_track\x18\x04 \x03(\x0b\x32!.spotify.metadata.proto.TopTracks\x12\x37\n\x0b\x61lbum_group\x18\x05 \x03(\x0b\x32\".spotify.metadata.proto.AlbumGroup\x12\x38\n\x0csingle_group\x18\x06 \x03(\x0b\x32\".spotify.metadata.proto.AlbumGroup\x12=\n\x11\x63ompilation_group\x18\x07 \x03(\x0b\x32\".spotify.metadata.proto.AlbumGroup\x12<\n\x10\x61ppears_on_group\x18\x08 \x03(\x0b\x32\".spotify.metadata.proto.AlbumGroup\x12\r\n\x05genre\x18\t \x03(\t\x12\x37\n\x0b\x65xternal_id\x18\n \x03(\x0b\x32\".spotify.metadata.proto.ExternalId\x12/\n\x08portrait\x18\x0b \x03(\x0b\x32\x1d.spotify.metadata.proto.Image\x12\x34\n\tbiography\x18\x0c \x03(\x0b\x32!.spotify.metadata.proto.Biography\x12?\n\x0f\x61\x63tivity_period\x18\r \x03(\x0b\x32&.spotify.metadata.proto.ActivityPeriod\x12\x38\n\x0brestriction\x18\x0e \x03(\x0b\x32#.spotify.metadata.proto.Restriction\x12/\n\x07related\x18\x0f \x03(\x0b\x32\x1e.spotify.metadata.proto.Artist\x12\x1f\n\x17is_portrait_album_cover\x18\x10 \x01(\x08\x12:\n\x0eportrait_group\x18\x11 \x01(\x0b\x32\".spotify.metadata.proto.ImageGroup\x12\x37\n\x0bsale_period\x18\x12 \x03(\x0b\x32\".spotify.metadata.proto.SalePeriod\x12:\n\x0c\x61vailability\x18\x14 \x03(\x0b\x32$.spotify.metadata.proto.Availability\"\xe8\x06\n\x05\x41lbum\x12\x0b\n\x03gid\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12.\n\x06\x61rtist\x18\x03 \x03(\x0b\x32\x1e.spotify.metadata.proto.Artist\x12\x30\n\x04type\x18\x04 \x01(\x0e\x32\".spotify.metadata.proto.Album.Type\x12\r\n\x05label\x18\x05 \x01(\t\x12*\n\x04\x64\x61te\x18\x06 \x01(\x0b\x32\x1c.spotify.metadata.proto.Date\x12\x12\n\npopularity\x18\x07 \x01(\x11\x12\r\n\x05genre\x18\x08 \x03(\t\x12,\n\x05\x63over\x18\t \x03(\x0b\x32\x1d.spotify.metadata.proto.Image\x12\x37\n\x0b\x65xternal_id\x18\n \x03(\x0b\x32\".spotify.metadata.proto.ExternalId\x12*\n\x04\x64isc\x18\x0b \x03(\x0b\x32\x1c.spotify.metadata.proto.Disc\x12\x0e\n\x06review\x18\x0c \x03(\t\x12\x34\n\tcopyright\x18\r \x03(\x0b\x32!.spotify.metadata.proto.Copyright\x12\x38\n\x0brestriction\x18\x0e \x03(\x0b\x32#.spotify.metadata.proto.Restriction\x12.\n\x07related\x18\x0f \x03(\x0b\x32\x1d.spotify.metadata.proto.Album\x12\x37\n\x0bsale_period\x18\x10 \x03(\x0b\x32\".spotify.metadata.proto.SalePeriod\x12\x37\n\x0b\x63over_group\x18\x11 \x01(\x0b\x32\".spotify.metadata.proto.ImageGroup\x12\x16\n\x0eoriginal_title\x18\x12 \x01(\t\x12\x15\n\rversion_title\x18\x13 \x01(\t\x12\x10\n\x08type_str\x18\x14 \x01(\t\x12:\n\x0c\x61vailability\x18\x17 \x03(\x0b\x32$.spotify.metadata.proto.Availability\"R\n\x04Type\x12\t\n\x05\x41LBUM\x10\x01\x12\n\n\x06SINGLE\x10\x02\x12\x0f\n\x0b\x43OMPILATION\x10\x03\x12\x06\n\x02\x45P\x10\x04\x12\r\n\tAUDIOBOOK\x10\x05\x12\x0b\n\x07PODCAST\x10\x06\"\xd5\x05\n\x05Track\x12\x0b\n\x03gid\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12,\n\x05\x61lbum\x18\x03 \x01(\x0b\x32\x1d.spotify.metadata.proto.Album\x12.\n\x06\x61rtist\x18\x04 \x03(\x0b\x32\x1e.spotify.metadata.proto.Artist\x12\x0e\n\x06number\x18\x05 \x01(\x11\x12\x13\n\x0b\x64isc_number\x18\x06 \x01(\x11\x12\x10\n\x08\x64uration\x18\x07 \x01(\x11\x12\x12\n\npopularity\x18\x08 \x01(\x11\x12\x10\n\x08\x65xplicit\x18\t \x01(\x08\x12\x37\n\x0b\x65xternal_id\x18\n \x03(\x0b\x32\".spotify.metadata.proto.ExternalId\x12\x38\n\x0brestriction\x18\x0b \x03(\x0b\x32#.spotify.metadata.proto.Restriction\x12/\n\x04\x66ile\x18\x0c \x03(\x0b\x32!.spotify.metadata.proto.AudioFile\x12\x32\n\x0b\x61lternative\x18\r \x03(\x0b\x32\x1d.spotify.metadata.proto.Track\x12\x37\n\x0bsale_period\x18\x0e \x03(\x0b\x32\".spotify.metadata.proto.SalePeriod\x12\x32\n\x07preview\x18\x0f \x03(\x0b\x32!.spotify.metadata.proto.AudioFile\x12\x0c\n\x04tags\x18\x10 \x03(\t\x12\x1f\n\x17\x65\x61rliest_live_timestamp\x18\x11 \x01(\x03\x12\x12\n\nhas_lyrics\x18\x12 \x01(\x08\x12:\n\x0c\x61vailability\x18\x13 \x03(\x0b\x32$.spotify.metadata.proto.Availability\x12\x32\n\x08licensor\x18\x15 \x01(\x0b\x32 .spotify.metadata.proto.Licensor\"\xbf\x05\n\x04Show\x12\x0b\n\x03gid\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18@ \x01(\t\x12!\n\x15\x64\x65precated_popularity\x18\x41 \x01(\x11\x42\x02\x18\x01\x12\x11\n\tpublisher\x18\x42 \x01(\t\x12\x10\n\x08language\x18\x43 \x01(\t\x12\x10\n\x08\x65xplicit\x18\x44 \x01(\x08\x12\x37\n\x0b\x63over_image\x18\x45 \x01(\x0b\x32\".spotify.metadata.proto.ImageGroup\x12\x30\n\x07\x65pisode\x18\x46 \x03(\x0b\x32\x1f.spotify.metadata.proto.Episode\x12\x34\n\tcopyright\x18G \x03(\x0b\x32!.spotify.metadata.proto.Copyright\x12\x38\n\x0brestriction\x18H \x03(\x0b\x32#.spotify.metadata.proto.Restriction\x12\x0f\n\x07keyword\x18I \x03(\t\x12:\n\nmedia_type\x18J \x01(\x0e\x32&.spotify.metadata.proto.Show.MediaType\x12H\n\x11\x63onsumption_order\x18K \x01(\x0e\x32-.spotify.metadata.proto.Show.ConsumptionOrder\x12:\n\x0c\x61vailability\x18N \x03(\x0b\x32$.spotify.metadata.proto.Availability\x12\x13\n\x0btrailer_uri\x18S \x01(\t\",\n\tMediaType\x12\t\n\x05MIXED\x10\x00\x12\t\n\x05\x41UDIO\x10\x01\x12\t\n\x05VIDEO\x10\x02\"<\n\x10\x43onsumptionOrder\x12\x0e\n\nSEQUENTIAL\x10\x01\x12\x0c\n\x08\x45PISODIC\x10\x02\x12\n\n\x06RECENT\x10\x03\"\xf9\x06\n\x07\x45pisode\x12\x0b\n\x03gid\x18\x01 \x01(\x0c\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x10\n\x08\x64uration\x18\x07 \x01(\x11\x12\x30\n\x05\x61udio\x18\x0c \x03(\x0b\x32!.spotify.metadata.proto.AudioFile\x12\x13\n\x0b\x64\x65scription\x18@ \x01(\t\x12\x0e\n\x06number\x18\x41 \x01(\x11\x12\x32\n\x0cpublish_time\x18\x42 \x01(\x0b\x32\x1c.spotify.metadata.proto.Date\x12!\n\x15\x64\x65precated_popularity\x18\x43 \x01(\x11\x42\x02\x18\x01\x12\x37\n\x0b\x63over_image\x18\x44 \x01(\x0b\x32\".spotify.metadata.proto.ImageGroup\x12\x10\n\x08language\x18\x45 \x01(\t\x12\x10\n\x08\x65xplicit\x18\x46 \x01(\x08\x12*\n\x04show\x18G \x01(\x0b\x32\x1c.spotify.metadata.proto.Show\x12\x30\n\x05video\x18H \x03(\x0b\x32!.spotify.metadata.proto.VideoFile\x12\x38\n\rvideo_preview\x18I \x03(\x0b\x32!.spotify.metadata.proto.VideoFile\x12\x38\n\raudio_preview\x18J \x03(\x0b\x32!.spotify.metadata.proto.AudioFile\x12\x38\n\x0brestriction\x18K \x03(\x0b\x32#.spotify.metadata.proto.Restriction\x12\x38\n\x0c\x66reeze_frame\x18L \x01(\x0b\x32\".spotify.metadata.proto.ImageGroup\x12\x0f\n\x07keyword\x18M \x03(\t\x12!\n\x19\x61llow_background_playback\x18Q \x01(\x08\x12:\n\x0c\x61vailability\x18R \x03(\x0b\x32$.spotify.metadata.proto.Availability\x12\x14\n\x0c\x65xternal_url\x18S \x01(\t\x12\x39\n\x04type\x18W \x01(\x0e\x32+.spotify.metadata.proto.Episode.EpisodeType\"/\n\x0b\x45pisodeType\x12\x08\n\x04\x46ULL\x10\x00\x12\x0b\n\x07TRAILER\x10\x01\x12\t\n\x05\x42ONUS\x10\x02\"\x18\n\x08Licensor\x12\x0c\n\x04uuid\x18\x01 \x01(\x0c\"J\n\tTopTracks\x12\x0f\n\x07\x63ountry\x18\x01 \x01(\t\x12,\n\x05track\x18\x02 \x03(\x0b\x32\x1d.spotify.metadata.proto.Track\"F\n\x0e\x41\x63tivityPeriod\x12\x12\n\nstart_year\x18\x01 \x01(\x11\x12\x10\n\x08\x65nd_year\x18\x02 \x01(\x11\x12\x0e\n\x06\x64\x65\x63\x61\x64\x65\x18\x03 \x01(\x11\":\n\nAlbumGroup\x12,\n\x05\x61lbum\x18\x01 \x03(\x0b\x32\x1d.spotify.metadata.proto.Album\"N\n\x04\x44\x61te\x12\x0c\n\x04year\x18\x01 \x01(\x11\x12\r\n\x05month\x18\x02 \x01(\x11\x12\x0b\n\x03\x64\x61y\x18\x03 \x01(\x11\x12\x0c\n\x04hour\x18\x04 \x01(\x11\x12\x0e\n\x06minute\x18\x05 \x01(\x11\"\xa0\x01\n\x05Image\x12\x0f\n\x07\x66ile_id\x18\x01 \x01(\x0c\x12\x30\n\x04size\x18\x02 \x01(\x0e\x32\".spotify.metadata.proto.Image.Size\x12\r\n\x05width\x18\x03 \x01(\x11\x12\x0e\n\x06height\x18\x04 \x01(\x11\"5\n\x04Size\x12\x0b\n\x07\x44\x45\x46\x41ULT\x10\x00\x12\t\n\x05SMALL\x10\x01\x12\t\n\x05LARGE\x10\x02\x12\n\n\x06XLARGE\x10\x03\":\n\nImageGroup\x12,\n\x05image\x18\x01 \x03(\x0b\x32\x1d.spotify.metadata.proto.Image\"\x86\x01\n\tBiography\x12\x0c\n\x04text\x18\x01 \x01(\t\x12/\n\x08portrait\x18\x02 \x03(\x0b\x32\x1d.spotify.metadata.proto.Image\x12:\n\x0eportrait_group\x18\x03 \x03(\x0b\x32\".spotify.metadata.proto.ImageGroup\"R\n\x04\x44isc\x12\x0e\n\x06number\x18\x01 \x01(\x11\x12\x0c\n\x04name\x18\x02 \x01(\t\x12,\n\x05track\x18\x03 \x03(\x0b\x32\x1d.spotify.metadata.proto.Track\"e\n\tCopyright\x12\x34\n\x04type\x18\x01 \x01(\x0e\x32&.spotify.metadata.proto.Copyright.Type\x12\x0c\n\x04text\x18\x02 \x01(\t\"\x14\n\x04Type\x12\x05\n\x01P\x10\x00\x12\x05\n\x01\x43\x10\x01\"\xdf\x02\n\x0bRestriction\x12@\n\tcatalogue\x18\x01 \x03(\x0e\x32-.spotify.metadata.proto.Restriction.Catalogue\x12\x36\n\x04type\x18\x04 \x01(\x0e\x32(.spotify.metadata.proto.Restriction.Type\x12\x15\n\rcatalogue_str\x18\x05 \x03(\t\x12\x1b\n\x11\x63ountries_allowed\x18\x02 \x01(\tH\x00\x12\x1d\n\x13\x63ountries_forbidden\x18\x03 \x01(\tH\x00\"U\n\tCatalogue\x12\x06\n\x02\x41\x44\x10\x00\x12\x10\n\x0cSUBSCRIPTION\x10\x01\x12\x11\n\rCATALOGUE_ALL\x10\x02\x12\x0b\n\x07SHUFFLE\x10\x03\x12\x0e\n\nCOMMERCIAL\x10\x04\"\x15\n\x04Type\x12\r\n\tSTREAMING\x10\x00\x42\x15\n\x13\x63ountry_restriction\"R\n\x0c\x41vailability\x12\x15\n\rcatalogue_str\x18\x01 \x03(\t\x12+\n\x05start\x18\x02 \x01(\x0b\x32\x1c.spotify.metadata.proto.Date\"\x9e\x01\n\nSalePeriod\x12\x38\n\x0brestriction\x18\x01 \x03(\x0b\x32#.spotify.metadata.proto.Restriction\x12+\n\x05start\x18\x02 \x01(\x0b\x32\x1c.spotify.metadata.proto.Date\x12)\n\x03\x65nd\x18\x03 \x01(\x0b\x32\x1c.spotify.metadata.proto.Date\"&\n\nExternalId\x12\x0c\n\x04type\x18\x01 \x01(\t\x12\n\n\x02id\x18\x02 \x01(\t\"\x89\x02\n\tAudioFile\x12\x0f\n\x07\x66ile_id\x18\x01 \x01(\x0c\x12\x38\n\x06\x66ormat\x18\x02 \x01(\x0e\x32(.spotify.metadata.proto.AudioFile.Format\"\xb0\x01\n\x06\x46ormat\x12\x11\n\rOGG_VORBIS_96\x10\x00\x12\x12\n\x0eOGG_VORBIS_160\x10\x01\x12\x12\n\x0eOGG_VORBIS_320\x10\x02\x12\x0b\n\x07MP3_256\x10\x03\x12\x0b\n\x07MP3_320\x10\x04\x12\x0b\n\x07MP3_160\x10\x05\x12\n\n\x06MP3_96\x10\x06\x12\x0f\n\x0bMP3_160_ENC\x10\x07\x12\n\n\x06\x41\x41\x43_24\x10\x08\x12\n\n\x06\x41\x41\x43_48\x10\t\x12\x0f\n\x0b\x41\x41\x43_24_NORM\x10\x10\"\x1c\n\tVideoFile\x12\x0f\n\x07\x66ile_id\x18\x01 \x01(\x0c\x42\"\n\x14\x63om.spotify.metadataB\x08MetadataH\x02' +) + +_ALBUM_TYPE = _descriptor.EnumDescriptor( + name='Type', + full_name='spotify.metadata.proto.Album.Type', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='ALBUM', + index=0, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SINGLE', + index=1, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='COMPILATION', + index=2, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EP', + index=3, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AUDIOBOOK', + index=4, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='PODCAST', + index=5, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=1742, + serialized_end=1824, +) +_sym_db.RegisterEnumDescriptor(_ALBUM_TYPE) + +_SHOW_MEDIATYPE = _descriptor.EnumDescriptor( + name='MediaType', + full_name='spotify.metadata.proto.Show.MediaType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='MIXED', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AUDIO', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='VIDEO', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3152, + serialized_end=3196, +) +_sym_db.RegisterEnumDescriptor(_SHOW_MEDIATYPE) + +_SHOW_CONSUMPTIONORDER = _descriptor.EnumDescriptor( + name='ConsumptionOrder', + full_name='spotify.metadata.proto.Show.ConsumptionOrder', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='SEQUENTIAL', + index=0, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='EPISODIC', + index=1, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='RECENT', + index=2, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=3198, + serialized_end=3258, +) +_sym_db.RegisterEnumDescriptor(_SHOW_CONSUMPTIONORDER) + +_EPISODE_EPISODETYPE = _descriptor.EnumDescriptor( + name='EpisodeType', + full_name='spotify.metadata.proto.Episode.EpisodeType', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='FULL', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='TRAILER', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='BONUS', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4103, + serialized_end=4150, +) +_sym_db.RegisterEnumDescriptor(_EPISODE_EPISODETYPE) + +_IMAGE_SIZE = _descriptor.EnumDescriptor( + name='Size', + full_name='spotify.metadata.proto.Image.Size', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='DEFAULT', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SMALL', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='LARGE', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='XLARGE', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4574, + serialized_end=4627, +) +_sym_db.RegisterEnumDescriptor(_IMAGE_SIZE) + +_COPYRIGHT_TYPE = _descriptor.EnumDescriptor( + name='Type', + full_name='spotify.metadata.proto.Copyright.Type', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='P', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='C', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=4991, + serialized_end=5011, +) +_sym_db.RegisterEnumDescriptor(_COPYRIGHT_TYPE) + +_RESTRICTION_CATALOGUE = _descriptor.EnumDescriptor( + name='Catalogue', + full_name='spotify.metadata.proto.Restriction.Catalogue', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='AD', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SUBSCRIPTION', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='CATALOGUE_ALL', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='SHUFFLE', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='COMMERCIAL', + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5234, + serialized_end=5319, +) +_sym_db.RegisterEnumDescriptor(_RESTRICTION_CATALOGUE) + +_RESTRICTION_TYPE = _descriptor.EnumDescriptor( + name='Type', + full_name='spotify.metadata.proto.Restriction.Type', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='STREAMING', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5321, + serialized_end=5342, +) +_sym_db.RegisterEnumDescriptor(_RESTRICTION_TYPE) + +_AUDIOFILE_FORMAT = _descriptor.EnumDescriptor( + name='Format', + full_name='spotify.metadata.proto.AudioFile.Format', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='OGG_VORBIS_96', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OGG_VORBIS_160', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='OGG_VORBIS_320', + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='MP3_256', + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='MP3_320', + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='MP3_160', + index=5, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='MP3_96', + index=6, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='MP3_160_ENC', + index=7, + number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AAC_24', + index=8, + number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AAC_48', + index=9, + number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='AAC_24_NORM', + index=10, + number=16, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=5742, + serialized_end=5918, +) +_sym_db.RegisterEnumDescriptor(_AUDIOFILE_FORMAT) + +_ARTIST = _descriptor.Descriptor( + name='Artist', + full_name='spotify.metadata.proto.Artist', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='gid', + full_name='spotify.metadata.proto.Artist.gid', + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='name', + full_name='spotify.metadata.proto.Artist.name', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='popularity', + full_name='spotify.metadata.proto.Artist.popularity', + index=2, + number=3, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='top_track', + full_name='spotify.metadata.proto.Artist.top_track', + index=3, + number=4, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='album_group', + full_name='spotify.metadata.proto.Artist.album_group', + index=4, + number=5, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='single_group', + full_name='spotify.metadata.proto.Artist.single_group', + index=5, + number=6, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='compilation_group', + full_name='spotify.metadata.proto.Artist.compilation_group', + index=6, + number=7, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='appears_on_group', + full_name='spotify.metadata.proto.Artist.appears_on_group', + index=7, + number=8, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='genre', + full_name='spotify.metadata.proto.Artist.genre', + index=8, + number=9, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='external_id', + full_name='spotify.metadata.proto.Artist.external_id', + index=9, + number=10, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='portrait', + full_name='spotify.metadata.proto.Artist.portrait', + index=10, + number=11, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='biography', + full_name='spotify.metadata.proto.Artist.biography', + index=11, + number=12, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='activity_period', + full_name='spotify.metadata.proto.Artist.activity_period', + index=12, + number=13, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='restriction', + full_name='spotify.metadata.proto.Artist.restriction', + index=13, + number=14, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='related', + full_name='spotify.metadata.proto.Artist.related', + index=14, + number=15, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_portrait_album_cover', + full_name='spotify.metadata.proto.Artist.is_portrait_album_cover', + index=15, + number=16, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='portrait_group', + full_name='spotify.metadata.proto.Artist.portrait_group', + index=16, + number=17, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='sale_period', + full_name='spotify.metadata.proto.Artist.sale_period', + index=17, + number=18, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='availability', + full_name='spotify.metadata.proto.Artist.availability', + index=18, + number=20, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=43, + serialized_end=949, +) + +_ALBUM = _descriptor.Descriptor( + name='Album', + full_name='spotify.metadata.proto.Album', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='gid', + full_name='spotify.metadata.proto.Album.gid', + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='name', + full_name='spotify.metadata.proto.Album.name', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='artist', + full_name='spotify.metadata.proto.Album.artist', + index=2, + number=3, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', + full_name='spotify.metadata.proto.Album.type', + index=3, + number=4, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=1, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='label', + full_name='spotify.metadata.proto.Album.label', + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='date', + full_name='spotify.metadata.proto.Album.date', + index=5, + number=6, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='popularity', + full_name='spotify.metadata.proto.Album.popularity', + index=6, + number=7, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='genre', + full_name='spotify.metadata.proto.Album.genre', + index=7, + number=8, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cover', + full_name='spotify.metadata.proto.Album.cover', + index=8, + number=9, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='external_id', + full_name='spotify.metadata.proto.Album.external_id', + index=9, + number=10, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disc', + full_name='spotify.metadata.proto.Album.disc', + index=10, + number=11, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='review', + full_name='spotify.metadata.proto.Album.review', + index=11, + number=12, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='copyright', + full_name='spotify.metadata.proto.Album.copyright', + index=12, + number=13, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='restriction', + full_name='spotify.metadata.proto.Album.restriction', + index=13, + number=14, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='related', + full_name='spotify.metadata.proto.Album.related', + index=14, + number=15, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='sale_period', + full_name='spotify.metadata.proto.Album.sale_period', + index=15, + number=16, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cover_group', + full_name='spotify.metadata.proto.Album.cover_group', + index=16, + number=17, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='original_title', + full_name='spotify.metadata.proto.Album.original_title', + index=17, + number=18, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='version_title', + full_name='spotify.metadata.proto.Album.version_title', + index=18, + number=19, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type_str', + full_name='spotify.metadata.proto.Album.type_str', + index=19, + number=20, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='availability', + full_name='spotify.metadata.proto.Album.availability', + index=20, + number=23, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[ + _ALBUM_TYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=952, + serialized_end=1824, +) + +_TRACK = _descriptor.Descriptor( + name='Track', + full_name='spotify.metadata.proto.Track', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='gid', + full_name='spotify.metadata.proto.Track.gid', + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='name', + full_name='spotify.metadata.proto.Track.name', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='album', + full_name='spotify.metadata.proto.Track.album', + index=2, + number=3, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='artist', + full_name='spotify.metadata.proto.Track.artist', + index=3, + number=4, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='number', + full_name='spotify.metadata.proto.Track.number', + index=4, + number=5, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disc_number', + full_name='spotify.metadata.proto.Track.disc_number', + index=5, + number=6, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='duration', + full_name='spotify.metadata.proto.Track.duration', + index=6, + number=7, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='popularity', + full_name='spotify.metadata.proto.Track.popularity', + index=7, + number=8, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='explicit', + full_name='spotify.metadata.proto.Track.explicit', + index=8, + number=9, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='external_id', + full_name='spotify.metadata.proto.Track.external_id', + index=9, + number=10, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='restriction', + full_name='spotify.metadata.proto.Track.restriction', + index=10, + number=11, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='file', + full_name='spotify.metadata.proto.Track.file', + index=11, + number=12, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='alternative', + full_name='spotify.metadata.proto.Track.alternative', + index=12, + number=13, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='sale_period', + full_name='spotify.metadata.proto.Track.sale_period', + index=13, + number=14, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='preview', + full_name='spotify.metadata.proto.Track.preview', + index=14, + number=15, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='tags', + full_name='spotify.metadata.proto.Track.tags', + index=15, + number=16, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='earliest_live_timestamp', + full_name='spotify.metadata.proto.Track.earliest_live_timestamp', + index=16, + number=17, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='has_lyrics', + full_name='spotify.metadata.proto.Track.has_lyrics', + index=17, + number=18, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='availability', + full_name='spotify.metadata.proto.Track.availability', + index=18, + number=19, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='licensor', + full_name='spotify.metadata.proto.Track.licensor', + index=19, + number=21, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=1827, + serialized_end=2552, +) + +_SHOW = _descriptor.Descriptor( + name='Show', + full_name='spotify.metadata.proto.Show', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='gid', + full_name='spotify.metadata.proto.Show.gid', + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='name', + full_name='spotify.metadata.proto.Show.name', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='description', + full_name='spotify.metadata.proto.Show.description', + index=2, + number=64, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated_popularity', + full_name='spotify.metadata.proto.Show.deprecated_popularity', + index=3, + number=65, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=b'\030\001', + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='publisher', + full_name='spotify.metadata.proto.Show.publisher', + index=4, + number=66, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='language', + full_name='spotify.metadata.proto.Show.language', + index=5, + number=67, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='explicit', + full_name='spotify.metadata.proto.Show.explicit', + index=6, + number=68, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cover_image', + full_name='spotify.metadata.proto.Show.cover_image', + index=7, + number=69, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='episode', + full_name='spotify.metadata.proto.Show.episode', + index=8, + number=70, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='copyright', + full_name='spotify.metadata.proto.Show.copyright', + index=9, + number=71, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='restriction', + full_name='spotify.metadata.proto.Show.restriction', + index=10, + number=72, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='keyword', + full_name='spotify.metadata.proto.Show.keyword', + index=11, + number=73, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='media_type', + full_name='spotify.metadata.proto.Show.media_type', + index=12, + number=74, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='consumption_order', + full_name='spotify.metadata.proto.Show.consumption_order', + index=13, + number=75, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=1, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='availability', + full_name='spotify.metadata.proto.Show.availability', + index=14, + number=78, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='trailer_uri', + full_name='spotify.metadata.proto.Show.trailer_uri', + index=15, + number=83, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[ + _SHOW_MEDIATYPE, + _SHOW_CONSUMPTIONORDER, + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=2555, + serialized_end=3258, +) + +_EPISODE = _descriptor.Descriptor( + name='Episode', + full_name='spotify.metadata.proto.Episode', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='gid', + full_name='spotify.metadata.proto.Episode.gid', + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='name', + full_name='spotify.metadata.proto.Episode.name', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='duration', + full_name='spotify.metadata.proto.Episode.duration', + index=2, + number=7, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='audio', + full_name='spotify.metadata.proto.Episode.audio', + index=3, + number=12, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='description', + full_name='spotify.metadata.proto.Episode.description', + index=4, + number=64, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='number', + full_name='spotify.metadata.proto.Episode.number', + index=5, + number=65, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='publish_time', + full_name='spotify.metadata.proto.Episode.publish_time', + index=6, + number=66, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='deprecated_popularity', + full_name='spotify.metadata.proto.Episode.deprecated_popularity', + index=7, + number=67, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=b'\030\001', + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cover_image', + full_name='spotify.metadata.proto.Episode.cover_image', + index=8, + number=68, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='language', + full_name='spotify.metadata.proto.Episode.language', + index=9, + number=69, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='explicit', + full_name='spotify.metadata.proto.Episode.explicit', + index=10, + number=70, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='show', + full_name='spotify.metadata.proto.Episode.show', + index=11, + number=71, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='video', + full_name='spotify.metadata.proto.Episode.video', + index=12, + number=72, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='video_preview', + full_name='spotify.metadata.proto.Episode.video_preview', + index=13, + number=73, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='audio_preview', + full_name='spotify.metadata.proto.Episode.audio_preview', + index=14, + number=74, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='restriction', + full_name='spotify.metadata.proto.Episode.restriction', + index=15, + number=75, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='freeze_frame', + full_name='spotify.metadata.proto.Episode.freeze_frame', + index=16, + number=76, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='keyword', + full_name='spotify.metadata.proto.Episode.keyword', + index=17, + number=77, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='allow_background_playback', + full_name= + 'spotify.metadata.proto.Episode.allow_background_playback', + index=18, + number=81, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='availability', + full_name='spotify.metadata.proto.Episode.availability', + index=19, + number=82, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='external_url', + full_name='spotify.metadata.proto.Episode.external_url', + index=20, + number=83, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', + full_name='spotify.metadata.proto.Episode.type', + index=21, + number=87, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[ + _EPISODE_EPISODETYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=3261, + serialized_end=4150, +) + +_LICENSOR = _descriptor.Descriptor( + name='Licensor', + full_name='spotify.metadata.proto.Licensor', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uuid', + full_name='spotify.metadata.proto.Licensor.uuid', + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=4152, + serialized_end=4176, +) + +_TOPTRACKS = _descriptor.Descriptor( + name='TopTracks', + full_name='spotify.metadata.proto.TopTracks', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='country', + full_name='spotify.metadata.proto.TopTracks.country', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='track', + full_name='spotify.metadata.proto.TopTracks.track', + index=1, + number=2, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=4178, + serialized_end=4252, +) + +_ACTIVITYPERIOD = _descriptor.Descriptor( + name='ActivityPeriod', + full_name='spotify.metadata.proto.ActivityPeriod', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='start_year', + full_name='spotify.metadata.proto.ActivityPeriod.start_year', + index=0, + number=1, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end_year', + full_name='spotify.metadata.proto.ActivityPeriod.end_year', + index=1, + number=2, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='decade', + full_name='spotify.metadata.proto.ActivityPeriod.decade', + index=2, + number=3, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=4254, + serialized_end=4324, +) + +_ALBUMGROUP = _descriptor.Descriptor( + name='AlbumGroup', + full_name='spotify.metadata.proto.AlbumGroup', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='album', + full_name='spotify.metadata.proto.AlbumGroup.album', + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=4326, + serialized_end=4384, +) + +_DATE = _descriptor.Descriptor( + name='Date', + full_name='spotify.metadata.proto.Date', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='year', + full_name='spotify.metadata.proto.Date.year', + index=0, + number=1, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='month', + full_name='spotify.metadata.proto.Date.month', + index=1, + number=2, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='day', + full_name='spotify.metadata.proto.Date.day', + index=2, + number=3, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='hour', + full_name='spotify.metadata.proto.Date.hour', + index=3, + number=4, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='minute', + full_name='spotify.metadata.proto.Date.minute', + index=4, + number=5, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=4386, + serialized_end=4464, +) + +_IMAGE = _descriptor.Descriptor( + name='Image', + full_name='spotify.metadata.proto.Image', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='file_id', + full_name='spotify.metadata.proto.Image.file_id', + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='size', + full_name='spotify.metadata.proto.Image.size', + index=1, + number=2, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='width', + full_name='spotify.metadata.proto.Image.width', + index=2, + number=3, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='height', + full_name='spotify.metadata.proto.Image.height', + index=3, + number=4, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[ + _IMAGE_SIZE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=4467, + serialized_end=4627, +) + +_IMAGEGROUP = _descriptor.Descriptor( + name='ImageGroup', + full_name='spotify.metadata.proto.ImageGroup', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='image', + full_name='spotify.metadata.proto.ImageGroup.image', + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=4629, + serialized_end=4687, +) + +_BIOGRAPHY = _descriptor.Descriptor( + name='Biography', + full_name='spotify.metadata.proto.Biography', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='text', + full_name='spotify.metadata.proto.Biography.text', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='portrait', + full_name='spotify.metadata.proto.Biography.portrait', + index=1, + number=2, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='portrait_group', + full_name='spotify.metadata.proto.Biography.portrait_group', + index=2, + number=3, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=4690, + serialized_end=4824, +) + +_DISC = _descriptor.Descriptor( + name='Disc', + full_name='spotify.metadata.proto.Disc', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='number', + full_name='spotify.metadata.proto.Disc.number', + index=0, + number=1, + type=17, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='name', + full_name='spotify.metadata.proto.Disc.name', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='track', + full_name='spotify.metadata.proto.Disc.track', + index=2, + number=3, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=4826, + serialized_end=4908, +) + +_COPYRIGHT = _descriptor.Descriptor( + name='Copyright', + full_name='spotify.metadata.proto.Copyright', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='type', + full_name='spotify.metadata.proto.Copyright.type', + index=0, + number=1, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='text', + full_name='spotify.metadata.proto.Copyright.text', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[ + _COPYRIGHT_TYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=4910, + serialized_end=5011, +) + +_RESTRICTION = _descriptor.Descriptor( + name='Restriction', + full_name='spotify.metadata.proto.Restriction', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='catalogue', + full_name='spotify.metadata.proto.Restriction.catalogue', + index=0, + number=1, + type=14, + cpp_type=8, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='type', + full_name='spotify.metadata.proto.Restriction.type', + index=1, + number=4, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='catalogue_str', + full_name='spotify.metadata.proto.Restriction.catalogue_str', + index=2, + number=5, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='countries_allowed', + full_name='spotify.metadata.proto.Restriction.countries_allowed', + index=3, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='countries_forbidden', + full_name='spotify.metadata.proto.Restriction.countries_forbidden', + index=4, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[ + _RESTRICTION_CATALOGUE, + _RESTRICTION_TYPE, + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[ + _descriptor.OneofDescriptor( + name='country_restriction', + full_name='spotify.metadata.proto.Restriction.country_restriction', + index=0, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[]), + ], + serialized_start=5014, + serialized_end=5365, +) + +_AVAILABILITY = _descriptor.Descriptor( + name='Availability', + full_name='spotify.metadata.proto.Availability', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='catalogue_str', + full_name='spotify.metadata.proto.Availability.catalogue_str', + index=0, + number=1, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='start', + full_name='spotify.metadata.proto.Availability.start', + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=5367, + serialized_end=5449, +) + +_SALEPERIOD = _descriptor.Descriptor( + name='SalePeriod', + full_name='spotify.metadata.proto.SalePeriod', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='restriction', + full_name='spotify.metadata.proto.SalePeriod.restriction', + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='start', + full_name='spotify.metadata.proto.SalePeriod.start', + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='end', + full_name='spotify.metadata.proto.SalePeriod.end', + index=2, + number=3, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=5452, + serialized_end=5610, +) + +_EXTERNALID = _descriptor.Descriptor( + name='ExternalId', + full_name='spotify.metadata.proto.ExternalId', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='type', + full_name='spotify.metadata.proto.ExternalId.type', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='id', + full_name='spotify.metadata.proto.ExternalId.id', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=5612, + serialized_end=5650, +) + +_AUDIOFILE = _descriptor.Descriptor( + name='AudioFile', + full_name='spotify.metadata.proto.AudioFile', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='file_id', + full_name='spotify.metadata.proto.AudioFile.file_id', + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='format', + full_name='spotify.metadata.proto.AudioFile.format', + index=1, + number=2, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[ + _AUDIOFILE_FORMAT, + ], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=5653, + serialized_end=5918, +) + +_VIDEOFILE = _descriptor.Descriptor( + name='VideoFile', + full_name='spotify.metadata.proto.VideoFile', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='file_id', + full_name='spotify.metadata.proto.VideoFile.file_id', + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=5920, + serialized_end=5948, +) + +_ARTIST.fields_by_name['top_track'].message_type = _TOPTRACKS +_ARTIST.fields_by_name['album_group'].message_type = _ALBUMGROUP +_ARTIST.fields_by_name['single_group'].message_type = _ALBUMGROUP +_ARTIST.fields_by_name['compilation_group'].message_type = _ALBUMGROUP +_ARTIST.fields_by_name['appears_on_group'].message_type = _ALBUMGROUP +_ARTIST.fields_by_name['external_id'].message_type = _EXTERNALID +_ARTIST.fields_by_name['portrait'].message_type = _IMAGE +_ARTIST.fields_by_name['biography'].message_type = _BIOGRAPHY +_ARTIST.fields_by_name['activity_period'].message_type = _ACTIVITYPERIOD +_ARTIST.fields_by_name['restriction'].message_type = _RESTRICTION +_ARTIST.fields_by_name['related'].message_type = _ARTIST +_ARTIST.fields_by_name['portrait_group'].message_type = _IMAGEGROUP +_ARTIST.fields_by_name['sale_period'].message_type = _SALEPERIOD +_ARTIST.fields_by_name['availability'].message_type = _AVAILABILITY +_ALBUM.fields_by_name['artist'].message_type = _ARTIST +_ALBUM.fields_by_name['type'].enum_type = _ALBUM_TYPE +_ALBUM.fields_by_name['date'].message_type = _DATE +_ALBUM.fields_by_name['cover'].message_type = _IMAGE +_ALBUM.fields_by_name['external_id'].message_type = _EXTERNALID +_ALBUM.fields_by_name['disc'].message_type = _DISC +_ALBUM.fields_by_name['copyright'].message_type = _COPYRIGHT +_ALBUM.fields_by_name['restriction'].message_type = _RESTRICTION +_ALBUM.fields_by_name['related'].message_type = _ALBUM +_ALBUM.fields_by_name['sale_period'].message_type = _SALEPERIOD +_ALBUM.fields_by_name['cover_group'].message_type = _IMAGEGROUP +_ALBUM.fields_by_name['availability'].message_type = _AVAILABILITY +_ALBUM_TYPE.containing_type = _ALBUM +_TRACK.fields_by_name['album'].message_type = _ALBUM +_TRACK.fields_by_name['artist'].message_type = _ARTIST +_TRACK.fields_by_name['external_id'].message_type = _EXTERNALID +_TRACK.fields_by_name['restriction'].message_type = _RESTRICTION +_TRACK.fields_by_name['file'].message_type = _AUDIOFILE +_TRACK.fields_by_name['alternative'].message_type = _TRACK +_TRACK.fields_by_name['sale_period'].message_type = _SALEPERIOD +_TRACK.fields_by_name['preview'].message_type = _AUDIOFILE +_TRACK.fields_by_name['availability'].message_type = _AVAILABILITY +_TRACK.fields_by_name['licensor'].message_type = _LICENSOR +_SHOW.fields_by_name['cover_image'].message_type = _IMAGEGROUP +_SHOW.fields_by_name['episode'].message_type = _EPISODE +_SHOW.fields_by_name['copyright'].message_type = _COPYRIGHT +_SHOW.fields_by_name['restriction'].message_type = _RESTRICTION +_SHOW.fields_by_name['media_type'].enum_type = _SHOW_MEDIATYPE +_SHOW.fields_by_name['consumption_order'].enum_type = _SHOW_CONSUMPTIONORDER +_SHOW.fields_by_name['availability'].message_type = _AVAILABILITY +_SHOW_MEDIATYPE.containing_type = _SHOW +_SHOW_CONSUMPTIONORDER.containing_type = _SHOW +_EPISODE.fields_by_name['audio'].message_type = _AUDIOFILE +_EPISODE.fields_by_name['publish_time'].message_type = _DATE +_EPISODE.fields_by_name['cover_image'].message_type = _IMAGEGROUP +_EPISODE.fields_by_name['show'].message_type = _SHOW +_EPISODE.fields_by_name['video'].message_type = _VIDEOFILE +_EPISODE.fields_by_name['video_preview'].message_type = _VIDEOFILE +_EPISODE.fields_by_name['audio_preview'].message_type = _AUDIOFILE +_EPISODE.fields_by_name['restriction'].message_type = _RESTRICTION +_EPISODE.fields_by_name['freeze_frame'].message_type = _IMAGEGROUP +_EPISODE.fields_by_name['availability'].message_type = _AVAILABILITY +_EPISODE.fields_by_name['type'].enum_type = _EPISODE_EPISODETYPE +_EPISODE_EPISODETYPE.containing_type = _EPISODE +_TOPTRACKS.fields_by_name['track'].message_type = _TRACK +_ALBUMGROUP.fields_by_name['album'].message_type = _ALBUM +_IMAGE.fields_by_name['size'].enum_type = _IMAGE_SIZE +_IMAGE_SIZE.containing_type = _IMAGE +_IMAGEGROUP.fields_by_name['image'].message_type = _IMAGE +_BIOGRAPHY.fields_by_name['portrait'].message_type = _IMAGE +_BIOGRAPHY.fields_by_name['portrait_group'].message_type = _IMAGEGROUP +_DISC.fields_by_name['track'].message_type = _TRACK +_COPYRIGHT.fields_by_name['type'].enum_type = _COPYRIGHT_TYPE +_COPYRIGHT_TYPE.containing_type = _COPYRIGHT +_RESTRICTION.fields_by_name['catalogue'].enum_type = _RESTRICTION_CATALOGUE +_RESTRICTION.fields_by_name['type'].enum_type = _RESTRICTION_TYPE +_RESTRICTION_CATALOGUE.containing_type = _RESTRICTION +_RESTRICTION_TYPE.containing_type = _RESTRICTION +_RESTRICTION.oneofs_by_name['country_restriction'].fields.append( + _RESTRICTION.fields_by_name['countries_allowed']) +_RESTRICTION.fields_by_name[ + 'countries_allowed'].containing_oneof = _RESTRICTION.oneofs_by_name[ + 'country_restriction'] +_RESTRICTION.oneofs_by_name['country_restriction'].fields.append( + _RESTRICTION.fields_by_name['countries_forbidden']) +_RESTRICTION.fields_by_name[ + 'countries_forbidden'].containing_oneof = _RESTRICTION.oneofs_by_name[ + 'country_restriction'] +_AVAILABILITY.fields_by_name['start'].message_type = _DATE +_SALEPERIOD.fields_by_name['restriction'].message_type = _RESTRICTION +_SALEPERIOD.fields_by_name['start'].message_type = _DATE +_SALEPERIOD.fields_by_name['end'].message_type = _DATE +_AUDIOFILE.fields_by_name['format'].enum_type = _AUDIOFILE_FORMAT +_AUDIOFILE_FORMAT.containing_type = _AUDIOFILE +DESCRIPTOR.message_types_by_name['Artist'] = _ARTIST +DESCRIPTOR.message_types_by_name['Album'] = _ALBUM +DESCRIPTOR.message_types_by_name['Track'] = _TRACK +DESCRIPTOR.message_types_by_name['Show'] = _SHOW +DESCRIPTOR.message_types_by_name['Episode'] = _EPISODE +DESCRIPTOR.message_types_by_name['Licensor'] = _LICENSOR +DESCRIPTOR.message_types_by_name['TopTracks'] = _TOPTRACKS +DESCRIPTOR.message_types_by_name['ActivityPeriod'] = _ACTIVITYPERIOD +DESCRIPTOR.message_types_by_name['AlbumGroup'] = _ALBUMGROUP +DESCRIPTOR.message_types_by_name['Date'] = _DATE +DESCRIPTOR.message_types_by_name['Image'] = _IMAGE +DESCRIPTOR.message_types_by_name['ImageGroup'] = _IMAGEGROUP +DESCRIPTOR.message_types_by_name['Biography'] = _BIOGRAPHY +DESCRIPTOR.message_types_by_name['Disc'] = _DISC +DESCRIPTOR.message_types_by_name['Copyright'] = _COPYRIGHT +DESCRIPTOR.message_types_by_name['Restriction'] = _RESTRICTION +DESCRIPTOR.message_types_by_name['Availability'] = _AVAILABILITY +DESCRIPTOR.message_types_by_name['SalePeriod'] = _SALEPERIOD +DESCRIPTOR.message_types_by_name['ExternalId'] = _EXTERNALID +DESCRIPTOR.message_types_by_name['AudioFile'] = _AUDIOFILE +DESCRIPTOR.message_types_by_name['VideoFile'] = _VIDEOFILE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Artist = _reflection.GeneratedProtocolMessageType( + 'Artist', + (_message.Message, ), + { + 'DESCRIPTOR': _ARTIST, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Artist) + }) +_sym_db.RegisterMessage(Artist) + +Album = _reflection.GeneratedProtocolMessageType( + 'Album', + (_message.Message, ), + { + 'DESCRIPTOR': _ALBUM, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Album) + }) +_sym_db.RegisterMessage(Album) + +Track = _reflection.GeneratedProtocolMessageType( + 'Track', + (_message.Message, ), + { + 'DESCRIPTOR': _TRACK, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Track) + }) +_sym_db.RegisterMessage(Track) + +Show = _reflection.GeneratedProtocolMessageType( + 'Show', + (_message.Message, ), + { + 'DESCRIPTOR': _SHOW, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Show) + }) +_sym_db.RegisterMessage(Show) + +Episode = _reflection.GeneratedProtocolMessageType( + 'Episode', + (_message.Message, ), + { + 'DESCRIPTOR': _EPISODE, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Episode) + }) +_sym_db.RegisterMessage(Episode) + +Licensor = _reflection.GeneratedProtocolMessageType( + 'Licensor', + (_message.Message, ), + { + 'DESCRIPTOR': _LICENSOR, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Licensor) + }) +_sym_db.RegisterMessage(Licensor) + +TopTracks = _reflection.GeneratedProtocolMessageType( + 'TopTracks', + (_message.Message, ), + { + 'DESCRIPTOR': _TOPTRACKS, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.TopTracks) + }) +_sym_db.RegisterMessage(TopTracks) + +ActivityPeriod = _reflection.GeneratedProtocolMessageType( + 'ActivityPeriod', + (_message.Message, ), + { + 'DESCRIPTOR': _ACTIVITYPERIOD, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.ActivityPeriod) + }) +_sym_db.RegisterMessage(ActivityPeriod) + +AlbumGroup = _reflection.GeneratedProtocolMessageType( + 'AlbumGroup', + (_message.Message, ), + { + 'DESCRIPTOR': _ALBUMGROUP, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.AlbumGroup) + }) +_sym_db.RegisterMessage(AlbumGroup) + +Date = _reflection.GeneratedProtocolMessageType( + 'Date', + (_message.Message, ), + { + 'DESCRIPTOR': _DATE, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Date) + }) +_sym_db.RegisterMessage(Date) + +Image = _reflection.GeneratedProtocolMessageType( + 'Image', + (_message.Message, ), + { + 'DESCRIPTOR': _IMAGE, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Image) + }) +_sym_db.RegisterMessage(Image) + +ImageGroup = _reflection.GeneratedProtocolMessageType( + 'ImageGroup', + (_message.Message, ), + { + 'DESCRIPTOR': _IMAGEGROUP, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.ImageGroup) + }) +_sym_db.RegisterMessage(ImageGroup) + +Biography = _reflection.GeneratedProtocolMessageType( + 'Biography', + (_message.Message, ), + { + 'DESCRIPTOR': _BIOGRAPHY, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Biography) + }) +_sym_db.RegisterMessage(Biography) + +Disc = _reflection.GeneratedProtocolMessageType( + 'Disc', + (_message.Message, ), + { + 'DESCRIPTOR': _DISC, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Disc) + }) +_sym_db.RegisterMessage(Disc) + +Copyright = _reflection.GeneratedProtocolMessageType( + 'Copyright', + (_message.Message, ), + { + 'DESCRIPTOR': _COPYRIGHT, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Copyright) + }) +_sym_db.RegisterMessage(Copyright) + +Restriction = _reflection.GeneratedProtocolMessageType( + 'Restriction', + (_message.Message, ), + { + 'DESCRIPTOR': _RESTRICTION, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Restriction) + }) +_sym_db.RegisterMessage(Restriction) + +Availability = _reflection.GeneratedProtocolMessageType( + 'Availability', + (_message.Message, ), + { + 'DESCRIPTOR': _AVAILABILITY, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.Availability) + }) +_sym_db.RegisterMessage(Availability) + +SalePeriod = _reflection.GeneratedProtocolMessageType( + 'SalePeriod', + (_message.Message, ), + { + 'DESCRIPTOR': _SALEPERIOD, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.SalePeriod) + }) +_sym_db.RegisterMessage(SalePeriod) + +ExternalId = _reflection.GeneratedProtocolMessageType( + 'ExternalId', + (_message.Message, ), + { + 'DESCRIPTOR': _EXTERNALID, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.ExternalId) + }) +_sym_db.RegisterMessage(ExternalId) + +AudioFile = _reflection.GeneratedProtocolMessageType( + 'AudioFile', + (_message.Message, ), + { + 'DESCRIPTOR': _AUDIOFILE, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.AudioFile) + }) +_sym_db.RegisterMessage(AudioFile) + +VideoFile = _reflection.GeneratedProtocolMessageType( + 'VideoFile', + (_message.Message, ), + { + 'DESCRIPTOR': _VIDEOFILE, + '__module__': 'metadata_pb2' + # @@protoc_insertion_point(class_scope:spotify.metadata.proto.VideoFile) + }) +_sym_db.RegisterMessage(VideoFile) + +DESCRIPTOR._options = None +_SHOW.fields_by_name['deprecated_popularity']._options = None +_EPISODE.fields_by_name['deprecated_popularity']._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/PlayOrigin_pb2.py b/resources/lib/librespot/proto/PlayOrigin_pb2.py new file mode 100644 index 0000000..64b3efe --- /dev/null +++ b/resources/lib/librespot/proto/PlayOrigin_pb2.py @@ -0,0 +1,193 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: play_origin.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="play_origin.proto", + package="spotify.player.proto", + syntax="proto2", + serialized_options=b"\n\023com.spotify.contextH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x11play_origin.proto\x12\x14spotify.player.proto"\xbf\x01\n\nPlayOrigin\x12\x1a\n\x12\x66\x65\x61ture_identifier\x18\x01 \x01(\t\x12\x17\n\x0f\x66\x65\x61ture_version\x18\x02 \x01(\t\x12\x10\n\x08view_uri\x18\x03 \x01(\t\x12\x19\n\x11\x65xternal_referrer\x18\x04 \x01(\t\x12\x1b\n\x13referrer_identifier\x18\x05 \x01(\t\x12\x19\n\x11\x64\x65vice_identifier\x18\x06 \x01(\t\x12\x17\n\x0f\x66\x65\x61ture_classes\x18\x07 \x03(\tB\x17\n\x13\x63om.spotify.contextH\x02', +) + +_PLAYORIGIN = _descriptor.Descriptor( + name="PlayOrigin", + full_name="spotify.player.proto.PlayOrigin", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="feature_identifier", + full_name="spotify.player.proto.PlayOrigin.feature_identifier", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="feature_version", + full_name="spotify.player.proto.PlayOrigin.feature_version", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="view_uri", + full_name="spotify.player.proto.PlayOrigin.view_uri", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="external_referrer", + full_name="spotify.player.proto.PlayOrigin.external_referrer", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="referrer_identifier", + full_name="spotify.player.proto.PlayOrigin.referrer_identifier", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="device_identifier", + full_name="spotify.player.proto.PlayOrigin.device_identifier", + index=5, + number=6, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="feature_classes", + full_name="spotify.player.proto.PlayOrigin.feature_classes", + index=6, + number=7, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=44, + serialized_end=235, +) + +DESCRIPTOR.message_types_by_name["PlayOrigin"] = _PLAYORIGIN +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +PlayOrigin = _reflection.GeneratedProtocolMessageType( + "PlayOrigin", + (_message.Message, ), + { + "DESCRIPTOR": _PLAYORIGIN, + "__module__": "play_origin_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.PlayOrigin) + }, +) +_sym_db.RegisterMessage(PlayOrigin) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Playback_pb2.py b/resources/lib/librespot/proto/Playback_pb2.py new file mode 100644 index 0000000..881cfeb --- /dev/null +++ b/resources/lib/librespot/proto/Playback_pb2.py @@ -0,0 +1,162 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: playback.proto +"""Generated protocol buffer code.""" +import ContextTrack_pb2 as context__track__pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="playback.proto", + package="spotify.player.proto.transfer", + syntax="proto2", + serialized_options=b"\n\024com.spotify.transferH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x0eplayback.proto\x12\x1dspotify.player.proto.transfer\x1a\x13\x63ontext_track.proto"\xa5\x01\n\x08Playback\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12 \n\x18position_as_of_timestamp\x18\x02 \x01(\x05\x12\x16\n\x0eplayback_speed\x18\x03 \x01(\x01\x12\x11\n\tis_paused\x18\x04 \x01(\x08\x12\x39\n\rcurrent_track\x18\x05 \x01(\x0b\x32".spotify.player.proto.ContextTrackB\x18\n\x14\x63om.spotify.transferH\x02', + dependencies=[ + context__track__pb2.DESCRIPTOR, + ], +) + +_PLAYBACK = _descriptor.Descriptor( + name="Playback", + full_name="spotify.player.proto.transfer.Playback", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="timestamp", + full_name="spotify.player.proto.transfer.Playback.timestamp", + index=0, + number=1, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="position_as_of_timestamp", + full_name= + "spotify.player.proto.transfer.Playback.position_as_of_timestamp", + index=1, + number=2, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="playback_speed", + full_name="spotify.player.proto.transfer.Playback.playback_speed", + index=2, + number=3, + type=1, + cpp_type=5, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="is_paused", + full_name="spotify.player.proto.transfer.Playback.is_paused", + index=3, + number=4, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="current_track", + full_name="spotify.player.proto.transfer.Playback.current_track", + index=4, + number=5, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=71, + serialized_end=236, +) + +_PLAYBACK.fields_by_name[ + "current_track"].message_type = context__track__pb2._CONTEXTTRACK +DESCRIPTOR.message_types_by_name["Playback"] = _PLAYBACK +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Playback = _reflection.GeneratedProtocolMessageType( + "Playback", + (_message.Message, ), + { + "DESCRIPTOR": _PLAYBACK, + "__module__": "playback_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.transfer.Playback) + }, +) +_sym_db.RegisterMessage(Playback) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Player_pb2.py b/resources/lib/librespot/proto/Player_pb2.py new file mode 100644 index 0000000..cd267de --- /dev/null +++ b/resources/lib/librespot/proto/Player_pb2.py @@ -0,0 +1,1851 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: player.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name='player.proto', + package='connectstate', + syntax='proto3', + serialized_options=b'\n\030com.spotify.connectstateH\002', + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x0cplayer.proto\x12\x0c\x63onnectstate\"\xea\x08\n\x0bPlayerState\x12\x11\n\ttimestamp\x18\x01 \x01(\x03\x12\x13\n\x0b\x63ontext_uri\x18\x02 \x01(\t\x12\x13\n\x0b\x63ontext_url\x18\x03 \x01(\t\x12\x38\n\x14\x63ontext_restrictions\x18\x04 \x01(\x0b\x32\x1a.connectstate.Restrictions\x12-\n\x0bplay_origin\x18\x05 \x01(\x0b\x32\x18.connectstate.PlayOrigin\x12)\n\x05index\x18\x06 \x01(\x0b\x32\x1a.connectstate.ContextIndex\x12*\n\x05track\x18\x07 \x01(\x0b\x32\x1b.connectstate.ProvidedTrack\x12\x13\n\x0bplayback_id\x18\x08 \x01(\t\x12\x16\n\x0eplayback_speed\x18\t \x01(\x01\x12 \n\x18position_as_of_timestamp\x18\n \x01(\x03\x12\x10\n\x08\x64uration\x18\x0b \x01(\x03\x12\x12\n\nis_playing\x18\x0c \x01(\x08\x12\x11\n\tis_paused\x18\r \x01(\x08\x12\x14\n\x0cis_buffering\x18\x0e \x01(\x08\x12\x1b\n\x13is_system_initiated\x18\x0f \x01(\x08\x12\x33\n\x07options\x18\x10 \x01(\x0b\x32\".connectstate.ContextPlayerOptions\x12\x30\n\x0crestrictions\x18\x11 \x01(\x0b\x32\x1a.connectstate.Restrictions\x12\x30\n\x0csuppressions\x18\x12 \x01(\x0b\x32\x1a.connectstate.Suppressions\x12\x30\n\x0bprev_tracks\x18\x13 \x03(\x0b\x32\x1b.connectstate.ProvidedTrack\x12\x30\n\x0bnext_tracks\x18\x14 \x03(\x0b\x32\x1b.connectstate.ProvidedTrack\x12H\n\x10\x63ontext_metadata\x18\x15 \x03(\x0b\x32..connectstate.PlayerState.ContextMetadataEntry\x12\x42\n\rpage_metadata\x18\x16 \x03(\x0b\x32+.connectstate.PlayerState.PageMetadataEntry\x12\x12\n\nsession_id\x18\x17 \x01(\t\x12\x16\n\x0equeue_revision\x18\x18 \x01(\t\x12\x10\n\x08position\x18\x19 \x01(\x03\x12\x12\n\nentity_uri\x18\x1a \x01(\t\x12,\n\x07reverse\x18\x1b \x03(\x0b\x32\x1b.connectstate.ProvidedTrack\x12+\n\x06\x66uture\x18\x1c \x03(\x0b\x32\x1b.connectstate.ProvidedTrack\x1a\x36\n\x14\x43ontextMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\x1a\x33\n\x11PageMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"\xda\x02\n\rProvidedTrack\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x0b\n\x03uid\x18\x02 \x01(\t\x12;\n\x08metadata\x18\x03 \x03(\x0b\x32).connectstate.ProvidedTrack.MetadataEntry\x12\x0f\n\x07removed\x18\x04 \x03(\t\x12\x0f\n\x07\x62locked\x18\x05 \x03(\t\x12\x10\n\x08provider\x18\x06 \x01(\t\x12\x30\n\x0crestrictions\x18\x07 \x01(\x0b\x32\x1a.connectstate.Restrictions\x12\x11\n\talbum_uri\x18\x08 \x01(\t\x12\x18\n\x10\x64isallow_reasons\x18\t \x03(\t\x12\x12\n\nartist_uri\x18\n \x01(\t\x12\x1a\n\x12\x64isallow_undecided\x18\x0b \x03(\t\x1a/\n\rMetadataEntry\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t:\x02\x38\x01\"+\n\x0c\x43ontextIndex\x12\x0c\n\x04page\x18\x01 \x01(\r\x12\r\n\x05track\x18\x02 \x01(\r\"\x80\x08\n\x0cRestrictions\x12 \n\x18\x64isallow_pausing_reasons\x18\x01 \x03(\t\x12!\n\x19\x64isallow_resuming_reasons\x18\x02 \x03(\t\x12 \n\x18\x64isallow_seeking_reasons\x18\x03 \x03(\t\x12%\n\x1d\x64isallow_peeking_prev_reasons\x18\x04 \x03(\t\x12%\n\x1d\x64isallow_peeking_next_reasons\x18\x05 \x03(\t\x12&\n\x1e\x64isallow_skipping_prev_reasons\x18\x06 \x03(\t\x12&\n\x1e\x64isallow_skipping_next_reasons\x18\x07 \x03(\t\x12\x30\n(disallow_toggling_repeat_context_reasons\x18\x08 \x03(\t\x12.\n&disallow_toggling_repeat_track_reasons\x18\t \x03(\t\x12)\n!disallow_toggling_shuffle_reasons\x18\n \x03(\t\x12\"\n\x1a\x64isallow_set_queue_reasons\x18\x0b \x03(\t\x12.\n&disallow_interrupting_playback_reasons\x18\x0c \x03(\t\x12.\n&disallow_transferring_playback_reasons\x18\r \x03(\t\x12\'\n\x1f\x64isallow_remote_control_reasons\x18\x0e \x03(\t\x12\x33\n+disallow_inserting_into_next_tracks_reasons\x18\x0f \x03(\t\x12\x36\n.disallow_inserting_into_context_tracks_reasons\x18\x10 \x03(\t\x12\x32\n*disallow_reordering_in_next_tracks_reasons\x18\x11 \x03(\t\x12\x35\n-disallow_reordering_in_context_tracks_reasons\x18\x12 \x03(\t\x12\x32\n*disallow_removing_from_next_tracks_reasons\x18\x13 \x03(\t\x12\x35\n-disallow_removing_from_context_tracks_reasons\x18\x14 \x03(\t\x12)\n!disallow_updating_context_reasons\x18\x15 \x03(\t\x12 \n\x18\x64isallow_playing_reasons\x18\x16 \x03(\t\x12!\n\x19\x64isallow_stopping_reasons\x18\x17 \x03(\t\"\xbf\x01\n\nPlayOrigin\x12\x1a\n\x12\x66\x65\x61ture_identifier\x18\x01 \x01(\t\x12\x17\n\x0f\x66\x65\x61ture_version\x18\x02 \x01(\t\x12\x10\n\x08view_uri\x18\x03 \x01(\t\x12\x19\n\x11\x65xternal_referrer\x18\x04 \x01(\t\x12\x1b\n\x13referrer_identifier\x18\x05 \x01(\t\x12\x19\n\x11\x64\x65vice_identifier\x18\x06 \x01(\t\x12\x17\n\x0f\x66\x65\x61ture_classes\x18\x07 \x03(\t\"e\n\x14\x43ontextPlayerOptions\x12\x19\n\x11shuffling_context\x18\x01 \x01(\x08\x12\x19\n\x11repeating_context\x18\x02 \x01(\x08\x12\x17\n\x0frepeating_track\x18\x03 \x01(\x08\"!\n\x0cSuppressions\x12\x11\n\tproviders\x18\x01 \x03(\tB\x1c\n\x18\x63om.spotify.connectstateH\x02\x62\x06proto3' +) + +_PLAYERSTATE_CONTEXTMETADATAENTRY = _descriptor.Descriptor( + name='ContextMetadataEntry', + full_name='connectstate.PlayerState.ContextMetadataEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', + full_name='connectstate.PlayerState.ContextMetadataEntry.key', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', + full_name='connectstate.PlayerState.ContextMetadataEntry.value', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=b'8\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1054, + serialized_end=1108, +) + +_PLAYERSTATE_PAGEMETADATAENTRY = _descriptor.Descriptor( + name='PageMetadataEntry', + full_name='connectstate.PlayerState.PageMetadataEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', + full_name='connectstate.PlayerState.PageMetadataEntry.key', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', + full_name='connectstate.PlayerState.PageMetadataEntry.value', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=b'8\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1110, + serialized_end=1161, +) + +_PLAYERSTATE = _descriptor.Descriptor( + name='PlayerState', + full_name='connectstate.PlayerState', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='timestamp', + full_name='connectstate.PlayerState.timestamp', + index=0, + number=1, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='context_uri', + full_name='connectstate.PlayerState.context_uri', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='context_url', + full_name='connectstate.PlayerState.context_url', + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='context_restrictions', + full_name='connectstate.PlayerState.context_restrictions', + index=3, + number=4, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='play_origin', + full_name='connectstate.PlayerState.play_origin', + index=4, + number=5, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='index', + full_name='connectstate.PlayerState.index', + index=5, + number=6, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='track', + full_name='connectstate.PlayerState.track', + index=6, + number=7, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='playback_id', + full_name='connectstate.PlayerState.playback_id', + index=7, + number=8, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='playback_speed', + full_name='connectstate.PlayerState.playback_speed', + index=8, + number=9, + type=1, + cpp_type=5, + label=1, + has_default_value=False, + default_value=float(0), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='position_as_of_timestamp', + full_name='connectstate.PlayerState.position_as_of_timestamp', + index=9, + number=10, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='duration', + full_name='connectstate.PlayerState.duration', + index=10, + number=11, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_playing', + full_name='connectstate.PlayerState.is_playing', + index=11, + number=12, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_paused', + full_name='connectstate.PlayerState.is_paused', + index=12, + number=13, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_buffering', + full_name='connectstate.PlayerState.is_buffering', + index=13, + number=14, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='is_system_initiated', + full_name='connectstate.PlayerState.is_system_initiated', + index=14, + number=15, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='options', + full_name='connectstate.PlayerState.options', + index=15, + number=16, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='restrictions', + full_name='connectstate.PlayerState.restrictions', + index=16, + number=17, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='suppressions', + full_name='connectstate.PlayerState.suppressions', + index=17, + number=18, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='prev_tracks', + full_name='connectstate.PlayerState.prev_tracks', + index=18, + number=19, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='next_tracks', + full_name='connectstate.PlayerState.next_tracks', + index=19, + number=20, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='context_metadata', + full_name='connectstate.PlayerState.context_metadata', + index=20, + number=21, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='page_metadata', + full_name='connectstate.PlayerState.page_metadata', + index=21, + number=22, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='session_id', + full_name='connectstate.PlayerState.session_id', + index=22, + number=23, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='queue_revision', + full_name='connectstate.PlayerState.queue_revision', + index=23, + number=24, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='position', + full_name='connectstate.PlayerState.position', + index=24, + number=25, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='entity_uri', + full_name='connectstate.PlayerState.entity_uri', + index=25, + number=26, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='reverse', + full_name='connectstate.PlayerState.reverse', + index=26, + number=27, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='future', + full_name='connectstate.PlayerState.future', + index=27, + number=28, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[ + _PLAYERSTATE_CONTEXTMETADATAENTRY, + _PLAYERSTATE_PAGEMETADATAENTRY, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=31, + serialized_end=1161, +) + +_PROVIDEDTRACK_METADATAENTRY = _descriptor.Descriptor( + name='MetadataEntry', + full_name='connectstate.ProvidedTrack.MetadataEntry', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='key', + full_name='connectstate.ProvidedTrack.MetadataEntry.key', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='value', + full_name='connectstate.ProvidedTrack.MetadataEntry.value', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=b'8\001', + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1463, + serialized_end=1510, +) + +_PROVIDEDTRACK = _descriptor.Descriptor( + name='ProvidedTrack', + full_name='connectstate.ProvidedTrack', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uri', + full_name='connectstate.ProvidedTrack.uri', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='uid', + full_name='connectstate.ProvidedTrack.uid', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='metadata', + full_name='connectstate.ProvidedTrack.metadata', + index=2, + number=3, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='removed', + full_name='connectstate.ProvidedTrack.removed', + index=3, + number=4, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='blocked', + full_name='connectstate.ProvidedTrack.blocked', + index=4, + number=5, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='provider', + full_name='connectstate.ProvidedTrack.provider', + index=5, + number=6, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='restrictions', + full_name='connectstate.ProvidedTrack.restrictions', + index=6, + number=7, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='album_uri', + full_name='connectstate.ProvidedTrack.album_uri', + index=7, + number=8, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_reasons', + full_name='connectstate.ProvidedTrack.disallow_reasons', + index=8, + number=9, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='artist_uri', + full_name='connectstate.ProvidedTrack.artist_uri', + index=9, + number=10, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_undecided', + full_name='connectstate.ProvidedTrack.disallow_undecided', + index=10, + number=11, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[ + _PROVIDEDTRACK_METADATAENTRY, + ], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1164, + serialized_end=1510, +) + +_CONTEXTINDEX = _descriptor.Descriptor( + name='ContextIndex', + full_name='connectstate.ContextIndex', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='page', + full_name='connectstate.ContextIndex.page', + index=0, + number=1, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='track', + full_name='connectstate.ContextIndex.track', + index=1, + number=2, + type=13, + cpp_type=3, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1512, + serialized_end=1555, +) + +_RESTRICTIONS = _descriptor.Descriptor( + name='Restrictions', + full_name='connectstate.Restrictions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='disallow_pausing_reasons', + full_name='connectstate.Restrictions.disallow_pausing_reasons', + index=0, + number=1, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_resuming_reasons', + full_name='connectstate.Restrictions.disallow_resuming_reasons', + index=1, + number=2, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_seeking_reasons', + full_name='connectstate.Restrictions.disallow_seeking_reasons', + index=2, + number=3, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_peeking_prev_reasons', + full_name='connectstate.Restrictions.disallow_peeking_prev_reasons', + index=3, + number=4, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_peeking_next_reasons', + full_name='connectstate.Restrictions.disallow_peeking_next_reasons', + index=4, + number=5, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_skipping_prev_reasons', + full_name= + 'connectstate.Restrictions.disallow_skipping_prev_reasons', + index=5, + number=6, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_skipping_next_reasons', + full_name= + 'connectstate.Restrictions.disallow_skipping_next_reasons', + index=6, + number=7, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_toggling_repeat_context_reasons', + full_name= + 'connectstate.Restrictions.disallow_toggling_repeat_context_reasons', + index=7, + number=8, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_toggling_repeat_track_reasons', + full_name= + 'connectstate.Restrictions.disallow_toggling_repeat_track_reasons', + index=8, + number=9, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_toggling_shuffle_reasons', + full_name= + 'connectstate.Restrictions.disallow_toggling_shuffle_reasons', + index=9, + number=10, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_set_queue_reasons', + full_name='connectstate.Restrictions.disallow_set_queue_reasons', + index=10, + number=11, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_interrupting_playback_reasons', + full_name= + 'connectstate.Restrictions.disallow_interrupting_playback_reasons', + index=11, + number=12, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_transferring_playback_reasons', + full_name= + 'connectstate.Restrictions.disallow_transferring_playback_reasons', + index=12, + number=13, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_remote_control_reasons', + full_name= + 'connectstate.Restrictions.disallow_remote_control_reasons', + index=13, + number=14, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_inserting_into_next_tracks_reasons', + full_name= + 'connectstate.Restrictions.disallow_inserting_into_next_tracks_reasons', + index=14, + number=15, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_inserting_into_context_tracks_reasons', + full_name= + 'connectstate.Restrictions.disallow_inserting_into_context_tracks_reasons', + index=15, + number=16, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_reordering_in_next_tracks_reasons', + full_name= + 'connectstate.Restrictions.disallow_reordering_in_next_tracks_reasons', + index=16, + number=17, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_reordering_in_context_tracks_reasons', + full_name= + 'connectstate.Restrictions.disallow_reordering_in_context_tracks_reasons', + index=17, + number=18, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_removing_from_next_tracks_reasons', + full_name= + 'connectstate.Restrictions.disallow_removing_from_next_tracks_reasons', + index=18, + number=19, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_removing_from_context_tracks_reasons', + full_name= + 'connectstate.Restrictions.disallow_removing_from_context_tracks_reasons', + index=19, + number=20, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_updating_context_reasons', + full_name= + 'connectstate.Restrictions.disallow_updating_context_reasons', + index=20, + number=21, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_playing_reasons', + full_name='connectstate.Restrictions.disallow_playing_reasons', + index=21, + number=22, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='disallow_stopping_reasons', + full_name='connectstate.Restrictions.disallow_stopping_reasons', + index=22, + number=23, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=1558, + serialized_end=2582, +) + +_PLAYORIGIN = _descriptor.Descriptor( + name='PlayOrigin', + full_name='connectstate.PlayOrigin', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='feature_identifier', + full_name='connectstate.PlayOrigin.feature_identifier', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='feature_version', + full_name='connectstate.PlayOrigin.feature_version', + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='view_uri', + full_name='connectstate.PlayOrigin.view_uri', + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='external_referrer', + full_name='connectstate.PlayOrigin.external_referrer', + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='referrer_identifier', + full_name='connectstate.PlayOrigin.referrer_identifier', + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='device_identifier', + full_name='connectstate.PlayOrigin.device_identifier', + index=5, + number=6, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='feature_classes', + full_name='connectstate.PlayOrigin.feature_classes', + index=6, + number=7, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=2585, + serialized_end=2776, +) + +_CONTEXTPLAYEROPTIONS = _descriptor.Descriptor( + name='ContextPlayerOptions', + full_name='connectstate.ContextPlayerOptions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='shuffling_context', + full_name='connectstate.ContextPlayerOptions.shuffling_context', + index=0, + number=1, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='repeating_context', + full_name='connectstate.ContextPlayerOptions.repeating_context', + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='repeating_track', + full_name='connectstate.ContextPlayerOptions.repeating_track', + index=2, + number=3, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=2778, + serialized_end=2879, +) + +_SUPPRESSIONS = _descriptor.Descriptor( + name='Suppressions', + full_name='connectstate.Suppressions', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='providers', + full_name='connectstate.Suppressions.providers', + index=0, + number=1, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=2881, + serialized_end=2914, +) + +_PLAYERSTATE_CONTEXTMETADATAENTRY.containing_type = _PLAYERSTATE +_PLAYERSTATE_PAGEMETADATAENTRY.containing_type = _PLAYERSTATE +_PLAYERSTATE.fields_by_name[ + 'context_restrictions'].message_type = _RESTRICTIONS +_PLAYERSTATE.fields_by_name['play_origin'].message_type = _PLAYORIGIN +_PLAYERSTATE.fields_by_name['index'].message_type = _CONTEXTINDEX +_PLAYERSTATE.fields_by_name['track'].message_type = _PROVIDEDTRACK +_PLAYERSTATE.fields_by_name['options'].message_type = _CONTEXTPLAYEROPTIONS +_PLAYERSTATE.fields_by_name['restrictions'].message_type = _RESTRICTIONS +_PLAYERSTATE.fields_by_name['suppressions'].message_type = _SUPPRESSIONS +_PLAYERSTATE.fields_by_name['prev_tracks'].message_type = _PROVIDEDTRACK +_PLAYERSTATE.fields_by_name['next_tracks'].message_type = _PROVIDEDTRACK +_PLAYERSTATE.fields_by_name[ + 'context_metadata'].message_type = _PLAYERSTATE_CONTEXTMETADATAENTRY +_PLAYERSTATE.fields_by_name[ + 'page_metadata'].message_type = _PLAYERSTATE_PAGEMETADATAENTRY +_PLAYERSTATE.fields_by_name['reverse'].message_type = _PROVIDEDTRACK +_PLAYERSTATE.fields_by_name['future'].message_type = _PROVIDEDTRACK +_PROVIDEDTRACK_METADATAENTRY.containing_type = _PROVIDEDTRACK +_PROVIDEDTRACK.fields_by_name[ + 'metadata'].message_type = _PROVIDEDTRACK_METADATAENTRY +_PROVIDEDTRACK.fields_by_name['restrictions'].message_type = _RESTRICTIONS +DESCRIPTOR.message_types_by_name['PlayerState'] = _PLAYERSTATE +DESCRIPTOR.message_types_by_name['ProvidedTrack'] = _PROVIDEDTRACK +DESCRIPTOR.message_types_by_name['ContextIndex'] = _CONTEXTINDEX +DESCRIPTOR.message_types_by_name['Restrictions'] = _RESTRICTIONS +DESCRIPTOR.message_types_by_name['PlayOrigin'] = _PLAYORIGIN +DESCRIPTOR.message_types_by_name[ + 'ContextPlayerOptions'] = _CONTEXTPLAYEROPTIONS +DESCRIPTOR.message_types_by_name['Suppressions'] = _SUPPRESSIONS +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +PlayerState = _reflection.GeneratedProtocolMessageType( + 'PlayerState', + (_message.Message, ), + { + 'ContextMetadataEntry': + _reflection.GeneratedProtocolMessageType( + 'ContextMetadataEntry', + (_message.Message, ), + { + 'DESCRIPTOR': _PLAYERSTATE_CONTEXTMETADATAENTRY, + '__module__': 'player_pb2' + # @@protoc_insertion_point(class_scope:connectstate.PlayerState.ContextMetadataEntry) + }), + 'PageMetadataEntry': + _reflection.GeneratedProtocolMessageType( + 'PageMetadataEntry', + (_message.Message, ), + { + 'DESCRIPTOR': _PLAYERSTATE_PAGEMETADATAENTRY, + '__module__': 'player_pb2' + # @@protoc_insertion_point(class_scope:connectstate.PlayerState.PageMetadataEntry) + }), + 'DESCRIPTOR': + _PLAYERSTATE, + '__module__': + 'player_pb2' + # @@protoc_insertion_point(class_scope:connectstate.PlayerState) + }) +_sym_db.RegisterMessage(PlayerState) +_sym_db.RegisterMessage(PlayerState.ContextMetadataEntry) +_sym_db.RegisterMessage(PlayerState.PageMetadataEntry) + +ProvidedTrack = _reflection.GeneratedProtocolMessageType( + 'ProvidedTrack', + (_message.Message, ), + { + 'MetadataEntry': + _reflection.GeneratedProtocolMessageType( + 'MetadataEntry', + (_message.Message, ), + { + 'DESCRIPTOR': _PROVIDEDTRACK_METADATAENTRY, + '__module__': 'player_pb2' + # @@protoc_insertion_point(class_scope:connectstate.ProvidedTrack.MetadataEntry) + }), + 'DESCRIPTOR': + _PROVIDEDTRACK, + '__module__': + 'player_pb2' + # @@protoc_insertion_point(class_scope:connectstate.ProvidedTrack) + }) +_sym_db.RegisterMessage(ProvidedTrack) +_sym_db.RegisterMessage(ProvidedTrack.MetadataEntry) + +ContextIndex = _reflection.GeneratedProtocolMessageType( + 'ContextIndex', + (_message.Message, ), + { + 'DESCRIPTOR': _CONTEXTINDEX, + '__module__': 'player_pb2' + # @@protoc_insertion_point(class_scope:connectstate.ContextIndex) + }) +_sym_db.RegisterMessage(ContextIndex) + +Restrictions = _reflection.GeneratedProtocolMessageType( + 'Restrictions', + (_message.Message, ), + { + 'DESCRIPTOR': _RESTRICTIONS, + '__module__': 'player_pb2' + # @@protoc_insertion_point(class_scope:connectstate.Restrictions) + }) +_sym_db.RegisterMessage(Restrictions) + +PlayOrigin = _reflection.GeneratedProtocolMessageType( + 'PlayOrigin', + (_message.Message, ), + { + 'DESCRIPTOR': _PLAYORIGIN, + '__module__': 'player_pb2' + # @@protoc_insertion_point(class_scope:connectstate.PlayOrigin) + }) +_sym_db.RegisterMessage(PlayOrigin) + +ContextPlayerOptions = _reflection.GeneratedProtocolMessageType( + 'ContextPlayerOptions', + (_message.Message, ), + { + 'DESCRIPTOR': _CONTEXTPLAYEROPTIONS, + '__module__': 'player_pb2' + # @@protoc_insertion_point(class_scope:connectstate.ContextPlayerOptions) + }) +_sym_db.RegisterMessage(ContextPlayerOptions) + +Suppressions = _reflection.GeneratedProtocolMessageType( + 'Suppressions', + (_message.Message, ), + { + 'DESCRIPTOR': _SUPPRESSIONS, + '__module__': 'player_pb2' + # @@protoc_insertion_point(class_scope:connectstate.Suppressions) + }) +_sym_db.RegisterMessage(Suppressions) + +DESCRIPTOR._options = None +_PLAYERSTATE_CONTEXTMETADATAENTRY._options = None +_PLAYERSTATE_PAGEMETADATAENTRY._options = None +_PROVIDEDTRACK_METADATAENTRY._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Playlist4External_pb2.py b/resources/lib/librespot/proto/Playlist4External_pb2.py new file mode 100644 index 0000000..f49f4ff --- /dev/null +++ b/resources/lib/librespot/proto/Playlist4External_pb2.py @@ -0,0 +1,3221 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: playlist4_external.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import enum_type_wrapper + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="playlist4_external.proto", + package="spotify.playlist4.proto", + syntax="proto2", + serialized_options= + b"\n\025com.spotify.playlist4B\021Playlist4ApiProtoH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x18playlist4_external.proto\x12\x17spotify.playlist4.proto"P\n\x04Item\x12\x0b\n\x03uri\x18\x01 \x02(\t\x12;\n\nattributes\x18\x02 \x01(\x0b\x32\'.spotify.playlist4.proto.ItemAttributes"\x94\x01\n\x08MetaItem\x12\x10\n\x08revision\x18\x01 \x01(\x0c\x12;\n\nattributes\x18\x02 \x01(\x0b\x32\'.spotify.playlist4.proto.ListAttributes\x12\x0e\n\x06length\x18\x03 \x01(\x05\x12\x11\n\ttimestamp\x18\x04 \x01(\x03\x12\x16\n\x0eowner_username\x18\x05 \x01(\t"\x90\x01\n\tListItems\x12\x0b\n\x03pos\x18\x01 \x02(\x05\x12\x11\n\ttruncated\x18\x02 \x02(\x08\x12,\n\x05items\x18\x03 \x03(\x0b\x32\x1d.spotify.playlist4.proto.Item\x12\x35\n\nmeta_items\x18\x04 \x03(\x0b\x32!.spotify.playlist4.proto.MetaItem"1\n\x13\x46ormatListAttribute\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t"\xf6\x01\n\x0eListAttributes\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x13\n\x0b\x64\x65scription\x18\x02 \x01(\t\x12\x0f\n\x07picture\x18\x03 \x01(\x0c\x12\x15\n\rcollaborative\x18\x04 \x01(\x08\x12\x13\n\x0bpl3_version\x18\x05 \x01(\t\x12\x18\n\x10\x64\x65leted_by_owner\x18\x06 \x01(\x08\x12\x11\n\tclient_id\x18\n \x01(\t\x12\x0e\n\x06\x66ormat\x18\x0b \x01(\t\x12G\n\x11\x66ormat_attributes\x18\x0c \x03(\x0b\x32,.spotify.playlist4.proto.FormatListAttribute"\xb0\x01\n\x0eItemAttributes\x12\x10\n\x08\x61\x64\x64\x65\x64_by\x18\x01 \x01(\t\x12\x11\n\ttimestamp\x18\x02 \x01(\x03\x12\x0f\n\x07seen_at\x18\t \x01(\x03\x12\x0e\n\x06public\x18\n \x01(\x08\x12G\n\x11\x66ormat_attributes\x18\x0b \x03(\x0b\x32,.spotify.playlist4.proto.FormatListAttribute\x12\x0f\n\x07item_id\x18\x0c \x01(\x0c"l\n\x03\x41\x64\x64\x12\x12\n\nfrom_index\x18\x01 \x01(\x05\x12,\n\x05items\x18\x02 \x03(\x0b\x32\x1d.spotify.playlist4.proto.Item\x12\x10\n\x08\x61\x64\x64_last\x18\x04 \x01(\x08\x12\x11\n\tadd_first\x18\x05 \x01(\x08"m\n\x03Rem\x12\x12\n\nfrom_index\x18\x01 \x01(\x05\x12\x0e\n\x06length\x18\x02 \x01(\x05\x12,\n\x05items\x18\x03 \x03(\x0b\x32\x1d.spotify.playlist4.proto.Item\x12\x14\n\x0citems_as_key\x18\x07 \x01(\x08";\n\x03Mov\x12\x12\n\nfrom_index\x18\x01 \x02(\x05\x12\x0e\n\x06length\x18\x02 \x02(\x05\x12\x10\n\x08to_index\x18\x03 \x02(\x05"\x93\x01\n\x1aItemAttributesPartialState\x12\x37\n\x06values\x18\x01 \x02(\x0b\x32\'.spotify.playlist4.proto.ItemAttributes\x12<\n\x08no_value\x18\x02 \x03(\x0e\x32*.spotify.playlist4.proto.ItemAttributeKind"\x93\x01\n\x1aListAttributesPartialState\x12\x37\n\x06values\x18\x01 \x02(\x0b\x32\'.spotify.playlist4.proto.ListAttributes\x12<\n\x08no_value\x18\x02 \x03(\x0e\x32*.spotify.playlist4.proto.ListAttributeKind"\xbf\x01\n\x14UpdateItemAttributes\x12\r\n\x05index\x18\x01 \x02(\x05\x12K\n\x0enew_attributes\x18\x02 \x02(\x0b\x32\x33.spotify.playlist4.proto.ItemAttributesPartialState\x12K\n\x0eold_attributes\x18\x03 \x01(\x0b\x32\x33.spotify.playlist4.proto.ItemAttributesPartialState"\xb0\x01\n\x14UpdateListAttributes\x12K\n\x0enew_attributes\x18\x01 \x02(\x0b\x32\x33.spotify.playlist4.proto.ListAttributesPartialState\x12K\n\x0eold_attributes\x18\x02 \x01(\x0b\x32\x33.spotify.playlist4.proto.ListAttributesPartialState"\xc0\x03\n\x02Op\x12.\n\x04kind\x18\x01 \x02(\x0e\x32 .spotify.playlist4.proto.Op.Kind\x12)\n\x03\x61\x64\x64\x18\x02 \x01(\x0b\x32\x1c.spotify.playlist4.proto.Add\x12)\n\x03rem\x18\x03 \x01(\x0b\x32\x1c.spotify.playlist4.proto.Rem\x12)\n\x03mov\x18\x04 \x01(\x0b\x32\x1c.spotify.playlist4.proto.Mov\x12M\n\x16update_item_attributes\x18\x05 \x01(\x0b\x32-.spotify.playlist4.proto.UpdateItemAttributes\x12M\n\x16update_list_attributes\x18\x06 \x01(\x0b\x32-.spotify.playlist4.proto.UpdateListAttributes"k\n\x04Kind\x12\x10\n\x0cKIND_UNKNOWN\x10\x00\x12\x07\n\x03\x41\x44\x44\x10\x02\x12\x07\n\x03REM\x10\x03\x12\x07\n\x03MOV\x10\x04\x12\x1a\n\x16UPDATE_ITEM_ATTRIBUTES\x10\x05\x12\x1a\n\x16UPDATE_LIST_ATTRIBUTES\x10\x06"2\n\x06OpList\x12(\n\x03ops\x18\x01 \x03(\x0b\x32\x1b.spotify.playlist4.proto.Op"\xd5\x01\n\nChangeInfo\x12\x0c\n\x04user\x18\x01 \x01(\t\x12\x11\n\ttimestamp\x18\x02 \x01(\x03\x12\r\n\x05\x61\x64min\x18\x03 \x01(\x08\x12\x0c\n\x04undo\x18\x04 \x01(\x08\x12\x0c\n\x04redo\x18\x05 \x01(\x08\x12\r\n\x05merge\x18\x06 \x01(\x08\x12\x12\n\ncompressed\x18\x07 \x01(\x08\x12\x11\n\tmigration\x18\x08 \x01(\x08\x12\x10\n\x08split_id\x18\t \x01(\x05\x12\x33\n\x06source\x18\n \x01(\x0b\x32#.spotify.playlist4.proto.SourceInfo"\xe8\x01\n\nSourceInfo\x12:\n\x06\x63lient\x18\x01 \x01(\x0e\x32*.spotify.playlist4.proto.SourceInfo.Client\x12\x0b\n\x03\x61pp\x18\x03 \x01(\t\x12\x0e\n\x06source\x18\x04 \x01(\t\x12\x0f\n\x07version\x18\x05 \x01(\t"p\n\x06\x43lient\x12\x12\n\x0e\x43LIENT_UNKNOWN\x10\x00\x12\x11\n\rNATIVE_HERMES\x10\x01\x12\n\n\x06\x43LIENT\x10\x02\x12\n\n\x06PYTHON\x10\x03\x12\x08\n\x04JAVA\x10\x04\x12\r\n\tWEBPLAYER\x10\x05\x12\x0e\n\nLIBSPOTIFY\x10\x06"z\n\x05\x44\x65lta\x12\x14\n\x0c\x62\x61se_version\x18\x01 \x01(\x0c\x12(\n\x03ops\x18\x02 \x03(\x0b\x32\x1b.spotify.playlist4.proto.Op\x12\x31\n\x04info\x18\x04 \x01(\x0b\x32#.spotify.playlist4.proto.ChangeInfo"\\\n\x04\x44iff\x12\x15\n\rfrom_revision\x18\x01 \x02(\x0c\x12(\n\x03ops\x18\x02 \x03(\x0b\x32\x1b.spotify.playlist4.proto.Op\x12\x13\n\x0bto_revision\x18\x03 \x02(\x0c"\xa0\x01\n\x0bListChanges\x12\x15\n\rbase_revision\x18\x01 \x01(\x0c\x12.\n\x06\x64\x65ltas\x18\x02 \x03(\x0b\x32\x1e.spotify.playlist4.proto.Delta\x12 \n\x18want_resulting_revisions\x18\x03 \x01(\x08\x12\x18\n\x10want_sync_result\x18\x04 \x01(\x08\x12\x0e\n\x06nonces\x18\x06 \x03(\x03"\x8f\x03\n\x13SelectedListContent\x12\x10\n\x08revision\x18\x01 \x01(\x0c\x12\x0e\n\x06length\x18\x02 \x01(\x05\x12;\n\nattributes\x18\x03 \x01(\x0b\x32\'.spotify.playlist4.proto.ListAttributes\x12\x34\n\x08\x63ontents\x18\x05 \x01(\x0b\x32".spotify.playlist4.proto.ListItems\x12+\n\x04\x64iff\x18\x06 \x01(\x0b\x32\x1d.spotify.playlist4.proto.Diff\x12\x32\n\x0bsync_result\x18\x07 \x01(\x0b\x32\x1d.spotify.playlist4.proto.Diff\x12\x1b\n\x13resulting_revisions\x18\x08 \x03(\x0c\x12\x16\n\x0emultiple_heads\x18\t \x01(\x08\x12\x12\n\nup_to_date\x18\n \x01(\x08\x12\x0e\n\x06nonces\x18\x0e \x03(\x03\x12\x11\n\ttimestamp\x18\x0f \x01(\x03\x12\x16\n\x0eowner_username\x18\x10 \x01(\t"0\n\x0f\x43reateListReply\x12\x0b\n\x03uri\x18\x01 \x02(\x0c\x12\x10\n\x08revision\x18\x02 \x01(\x0c",\n\x0bModifyReply\x12\x0b\n\x03uri\x18\x01 \x02(\x0c\x12\x10\n\x08revision\x18\x02 \x01(\x0c" \n\x10SubscribeRequest\x12\x0c\n\x04uris\x18\x01 \x03(\x0c""\n\x12UnsubscribeRequest\x12\x0c\n\x04uris\x18\x01 \x03(\x0c"\x80\x01\n\x18PlaylistModificationInfo\x12\x0b\n\x03uri\x18\x01 \x01(\x0c\x12\x14\n\x0cnew_revision\x18\x02 \x01(\x0c\x12\x17\n\x0fparent_revision\x18\x03 \x01(\x0c\x12(\n\x03ops\x18\x04 \x03(\x0b\x32\x1b.spotify.playlist4.proto.Op*\xe6\x01\n\x11ListAttributeKind\x12\x10\n\x0cLIST_UNKNOWN\x10\x00\x12\r\n\tLIST_NAME\x10\x01\x12\x14\n\x10LIST_DESCRIPTION\x10\x02\x12\x10\n\x0cLIST_PICTURE\x10\x03\x12\x16\n\x12LIST_COLLABORATIVE\x10\x04\x12\x14\n\x10LIST_PL3_VERSION\x10\x05\x12\x19\n\x15LIST_DELETED_BY_OWNER\x10\x06\x12\x12\n\x0eLIST_CLIENT_ID\x10\n\x12\x0f\n\x0bLIST_FORMAT\x10\x0b\x12\x1a\n\x16LIST_FORMAT_ATTRIBUTES\x10\x0c*\x98\x01\n\x11ItemAttributeKind\x12\x10\n\x0cITEM_UNKNOWN\x10\x00\x12\x11\n\rITEM_ADDED_BY\x10\x01\x12\x12\n\x0eITEM_TIMESTAMP\x10\x02\x12\x10\n\x0cITEM_SEEN_AT\x10\t\x12\x0f\n\x0bITEM_PUBLIC\x10\n\x12\x1a\n\x16ITEM_FORMAT_ATTRIBUTES\x10\x0b\x12\x0b\n\x07ITEM_ID\x10\x0c\x42,\n\x15\x63om.spotify.playlist4B\x11Playlist4ApiProtoH\x02', +) + +_LISTATTRIBUTEKIND = _descriptor.EnumDescriptor( + name="ListAttributeKind", + full_name="spotify.playlist4.proto.ListAttributeKind", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="LIST_UNKNOWN", + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="LIST_NAME", + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="LIST_DESCRIPTION", + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="LIST_PICTURE", + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="LIST_COLLABORATIVE", + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="LIST_PL3_VERSION", + index=5, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="LIST_DELETED_BY_OWNER", + index=6, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="LIST_CLIENT_ID", + index=7, + number=10, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="LIST_FORMAT", + index=8, + number=11, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="LIST_FORMAT_ATTRIBUTES", + index=9, + number=12, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=3902, + serialized_end=4132, +) +_sym_db.RegisterEnumDescriptor(_LISTATTRIBUTEKIND) + +ListAttributeKind = enum_type_wrapper.EnumTypeWrapper(_LISTATTRIBUTEKIND) +_ITEMATTRIBUTEKIND = _descriptor.EnumDescriptor( + name="ItemAttributeKind", + full_name="spotify.playlist4.proto.ItemAttributeKind", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="ITEM_UNKNOWN", + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="ITEM_ADDED_BY", + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="ITEM_TIMESTAMP", + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="ITEM_SEEN_AT", + index=3, + number=9, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="ITEM_PUBLIC", + index=4, + number=10, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="ITEM_FORMAT_ATTRIBUTES", + index=5, + number=11, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="ITEM_ID", + index=6, + number=12, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=4135, + serialized_end=4287, +) +_sym_db.RegisterEnumDescriptor(_ITEMATTRIBUTEKIND) + +ItemAttributeKind = enum_type_wrapper.EnumTypeWrapper(_ITEMATTRIBUTEKIND) +LIST_UNKNOWN = 0 +LIST_NAME = 1 +LIST_DESCRIPTION = 2 +LIST_PICTURE = 3 +LIST_COLLABORATIVE = 4 +LIST_PL3_VERSION = 5 +LIST_DELETED_BY_OWNER = 6 +LIST_CLIENT_ID = 10 +LIST_FORMAT = 11 +LIST_FORMAT_ATTRIBUTES = 12 +ITEM_UNKNOWN = 0 +ITEM_ADDED_BY = 1 +ITEM_TIMESTAMP = 2 +ITEM_SEEN_AT = 9 +ITEM_PUBLIC = 10 +ITEM_FORMAT_ATTRIBUTES = 11 +ITEM_ID = 12 + +_OP_KIND = _descriptor.EnumDescriptor( + name="Kind", + full_name="spotify.playlist4.proto.Op.Kind", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="KIND_UNKNOWN", + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="ADD", + index=1, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="REM", + index=2, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="MOV", + index=3, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="UPDATE_ITEM_ATTRIBUTES", + index=4, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="UPDATE_LIST_ATTRIBUTES", + index=5, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=2209, + serialized_end=2316, +) +_sym_db.RegisterEnumDescriptor(_OP_KIND) + +_SOURCEINFO_CLIENT = _descriptor.EnumDescriptor( + name="Client", + full_name="spotify.playlist4.proto.SourceInfo.Client", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="CLIENT_UNKNOWN", + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="NATIVE_HERMES", + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="CLIENT", + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="PYTHON", + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="JAVA", + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="WEBPLAYER", + index=5, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="LIBSPOTIFY", + index=6, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=2707, + serialized_end=2819, +) +_sym_db.RegisterEnumDescriptor(_SOURCEINFO_CLIENT) + +_ITEM = _descriptor.Descriptor( + name="Item", + full_name="spotify.playlist4.proto.Item", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="uri", + full_name="spotify.playlist4.proto.Item.uri", + index=0, + number=1, + type=9, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="attributes", + full_name="spotify.playlist4.proto.Item.attributes", + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=53, + serialized_end=133, +) + +_METAITEM = _descriptor.Descriptor( + name="MetaItem", + full_name="spotify.playlist4.proto.MetaItem", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="revision", + full_name="spotify.playlist4.proto.MetaItem.revision", + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="attributes", + full_name="spotify.playlist4.proto.MetaItem.attributes", + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="length", + full_name="spotify.playlist4.proto.MetaItem.length", + index=2, + number=3, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="timestamp", + full_name="spotify.playlist4.proto.MetaItem.timestamp", + index=3, + number=4, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="owner_username", + full_name="spotify.playlist4.proto.MetaItem.owner_username", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=136, + serialized_end=284, +) + +_LISTITEMS = _descriptor.Descriptor( + name="ListItems", + full_name="spotify.playlist4.proto.ListItems", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="pos", + full_name="spotify.playlist4.proto.ListItems.pos", + index=0, + number=1, + type=5, + cpp_type=1, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="truncated", + full_name="spotify.playlist4.proto.ListItems.truncated", + index=1, + number=2, + type=8, + cpp_type=7, + label=2, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="items", + full_name="spotify.playlist4.proto.ListItems.items", + index=2, + number=3, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="meta_items", + full_name="spotify.playlist4.proto.ListItems.meta_items", + index=3, + number=4, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=287, + serialized_end=431, +) + +_FORMATLISTATTRIBUTE = _descriptor.Descriptor( + name="FormatListAttribute", + full_name="spotify.playlist4.proto.FormatListAttribute", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="key", + full_name="spotify.playlist4.proto.FormatListAttribute.key", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="value", + full_name="spotify.playlist4.proto.FormatListAttribute.value", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=433, + serialized_end=482, +) + +_LISTATTRIBUTES = _descriptor.Descriptor( + name="ListAttributes", + full_name="spotify.playlist4.proto.ListAttributes", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="name", + full_name="spotify.playlist4.proto.ListAttributes.name", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="description", + full_name="spotify.playlist4.proto.ListAttributes.description", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="picture", + full_name="spotify.playlist4.proto.ListAttributes.picture", + index=2, + number=3, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="collaborative", + full_name="spotify.playlist4.proto.ListAttributes.collaborative", + index=3, + number=4, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="pl3_version", + full_name="spotify.playlist4.proto.ListAttributes.pl3_version", + index=4, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="deleted_by_owner", + full_name="spotify.playlist4.proto.ListAttributes.deleted_by_owner", + index=5, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="client_id", + full_name="spotify.playlist4.proto.ListAttributes.client_id", + index=6, + number=10, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="format", + full_name="spotify.playlist4.proto.ListAttributes.format", + index=7, + number=11, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="format_attributes", + full_name= + "spotify.playlist4.proto.ListAttributes.format_attributes", + index=8, + number=12, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=485, + serialized_end=731, +) + +_ITEMATTRIBUTES = _descriptor.Descriptor( + name="ItemAttributes", + full_name="spotify.playlist4.proto.ItemAttributes", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="added_by", + full_name="spotify.playlist4.proto.ItemAttributes.added_by", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="timestamp", + full_name="spotify.playlist4.proto.ItemAttributes.timestamp", + index=1, + number=2, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="seen_at", + full_name="spotify.playlist4.proto.ItemAttributes.seen_at", + index=2, + number=9, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="public", + full_name="spotify.playlist4.proto.ItemAttributes.public", + index=3, + number=10, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="format_attributes", + full_name= + "spotify.playlist4.proto.ItemAttributes.format_attributes", + index=4, + number=11, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="item_id", + full_name="spotify.playlist4.proto.ItemAttributes.item_id", + index=5, + number=12, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=734, + serialized_end=910, +) + +_ADD = _descriptor.Descriptor( + name="Add", + full_name="spotify.playlist4.proto.Add", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="from_index", + full_name="spotify.playlist4.proto.Add.from_index", + index=0, + number=1, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="items", + full_name="spotify.playlist4.proto.Add.items", + index=1, + number=2, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="add_last", + full_name="spotify.playlist4.proto.Add.add_last", + index=2, + number=4, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="add_first", + full_name="spotify.playlist4.proto.Add.add_first", + index=3, + number=5, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=912, + serialized_end=1020, +) + +_REM = _descriptor.Descriptor( + name="Rem", + full_name="spotify.playlist4.proto.Rem", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="from_index", + full_name="spotify.playlist4.proto.Rem.from_index", + index=0, + number=1, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="length", + full_name="spotify.playlist4.proto.Rem.length", + index=1, + number=2, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="items", + full_name="spotify.playlist4.proto.Rem.items", + index=2, + number=3, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="items_as_key", + full_name="spotify.playlist4.proto.Rem.items_as_key", + index=3, + number=7, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=1022, + serialized_end=1131, +) + +_MOV = _descriptor.Descriptor( + name="Mov", + full_name="spotify.playlist4.proto.Mov", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="from_index", + full_name="spotify.playlist4.proto.Mov.from_index", + index=0, + number=1, + type=5, + cpp_type=1, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="length", + full_name="spotify.playlist4.proto.Mov.length", + index=1, + number=2, + type=5, + cpp_type=1, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="to_index", + full_name="spotify.playlist4.proto.Mov.to_index", + index=2, + number=3, + type=5, + cpp_type=1, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=1133, + serialized_end=1192, +) + +_ITEMATTRIBUTESPARTIALSTATE = _descriptor.Descriptor( + name="ItemAttributesPartialState", + full_name="spotify.playlist4.proto.ItemAttributesPartialState", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="values", + full_name= + "spotify.playlist4.proto.ItemAttributesPartialState.values", + index=0, + number=1, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="no_value", + full_name= + "spotify.playlist4.proto.ItemAttributesPartialState.no_value", + index=1, + number=2, + type=14, + cpp_type=8, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=1195, + serialized_end=1342, +) + +_LISTATTRIBUTESPARTIALSTATE = _descriptor.Descriptor( + name="ListAttributesPartialState", + full_name="spotify.playlist4.proto.ListAttributesPartialState", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="values", + full_name= + "spotify.playlist4.proto.ListAttributesPartialState.values", + index=0, + number=1, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="no_value", + full_name= + "spotify.playlist4.proto.ListAttributesPartialState.no_value", + index=1, + number=2, + type=14, + cpp_type=8, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=1345, + serialized_end=1492, +) + +_UPDATEITEMATTRIBUTES = _descriptor.Descriptor( + name="UpdateItemAttributes", + full_name="spotify.playlist4.proto.UpdateItemAttributes", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="index", + full_name="spotify.playlist4.proto.UpdateItemAttributes.index", + index=0, + number=1, + type=5, + cpp_type=1, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="new_attributes", + full_name= + "spotify.playlist4.proto.UpdateItemAttributes.new_attributes", + index=1, + number=2, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="old_attributes", + full_name= + "spotify.playlist4.proto.UpdateItemAttributes.old_attributes", + index=2, + number=3, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=1495, + serialized_end=1686, +) + +_UPDATELISTATTRIBUTES = _descriptor.Descriptor( + name="UpdateListAttributes", + full_name="spotify.playlist4.proto.UpdateListAttributes", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="new_attributes", + full_name= + "spotify.playlist4.proto.UpdateListAttributes.new_attributes", + index=0, + number=1, + type=11, + cpp_type=10, + label=2, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="old_attributes", + full_name= + "spotify.playlist4.proto.UpdateListAttributes.old_attributes", + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=1689, + serialized_end=1865, +) + +_OP = _descriptor.Descriptor( + name="Op", + full_name="spotify.playlist4.proto.Op", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="kind", + full_name="spotify.playlist4.proto.Op.kind", + index=0, + number=1, + type=14, + cpp_type=8, + label=2, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="add", + full_name="spotify.playlist4.proto.Op.add", + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="rem", + full_name="spotify.playlist4.proto.Op.rem", + index=2, + number=3, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="mov", + full_name="spotify.playlist4.proto.Op.mov", + index=3, + number=4, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="update_item_attributes", + full_name="spotify.playlist4.proto.Op.update_item_attributes", + index=4, + number=5, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="update_list_attributes", + full_name="spotify.playlist4.proto.Op.update_list_attributes", + index=5, + number=6, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[ + _OP_KIND, + ], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=1868, + serialized_end=2316, +) + +_OPLIST = _descriptor.Descriptor( + name="OpList", + full_name="spotify.playlist4.proto.OpList", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="ops", + full_name="spotify.playlist4.proto.OpList.ops", + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=2318, + serialized_end=2368, +) + +_CHANGEINFO = _descriptor.Descriptor( + name="ChangeInfo", + full_name="spotify.playlist4.proto.ChangeInfo", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="user", + full_name="spotify.playlist4.proto.ChangeInfo.user", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="timestamp", + full_name="spotify.playlist4.proto.ChangeInfo.timestamp", + index=1, + number=2, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="admin", + full_name="spotify.playlist4.proto.ChangeInfo.admin", + index=2, + number=3, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="undo", + full_name="spotify.playlist4.proto.ChangeInfo.undo", + index=3, + number=4, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="redo", + full_name="spotify.playlist4.proto.ChangeInfo.redo", + index=4, + number=5, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="merge", + full_name="spotify.playlist4.proto.ChangeInfo.merge", + index=5, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="compressed", + full_name="spotify.playlist4.proto.ChangeInfo.compressed", + index=6, + number=7, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="migration", + full_name="spotify.playlist4.proto.ChangeInfo.migration", + index=7, + number=8, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="split_id", + full_name="spotify.playlist4.proto.ChangeInfo.split_id", + index=8, + number=9, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="source", + full_name="spotify.playlist4.proto.ChangeInfo.source", + index=9, + number=10, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=2371, + serialized_end=2584, +) + +_SOURCEINFO = _descriptor.Descriptor( + name="SourceInfo", + full_name="spotify.playlist4.proto.SourceInfo", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="client", + full_name="spotify.playlist4.proto.SourceInfo.client", + index=0, + number=1, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="app", + full_name="spotify.playlist4.proto.SourceInfo.app", + index=1, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="source", + full_name="spotify.playlist4.proto.SourceInfo.source", + index=2, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="version", + full_name="spotify.playlist4.proto.SourceInfo.version", + index=3, + number=5, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[ + _SOURCEINFO_CLIENT, + ], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=2587, + serialized_end=2819, +) + +_DELTA = _descriptor.Descriptor( + name="Delta", + full_name="spotify.playlist4.proto.Delta", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="base_version", + full_name="spotify.playlist4.proto.Delta.base_version", + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="ops", + full_name="spotify.playlist4.proto.Delta.ops", + index=1, + number=2, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="info", + full_name="spotify.playlist4.proto.Delta.info", + index=2, + number=4, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=2821, + serialized_end=2943, +) + +_DIFF = _descriptor.Descriptor( + name="Diff", + full_name="spotify.playlist4.proto.Diff", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="from_revision", + full_name="spotify.playlist4.proto.Diff.from_revision", + index=0, + number=1, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="ops", + full_name="spotify.playlist4.proto.Diff.ops", + index=1, + number=2, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="to_revision", + full_name="spotify.playlist4.proto.Diff.to_revision", + index=2, + number=3, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=2945, + serialized_end=3037, +) + +_LISTCHANGES = _descriptor.Descriptor( + name="ListChanges", + full_name="spotify.playlist4.proto.ListChanges", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="base_revision", + full_name="spotify.playlist4.proto.ListChanges.base_revision", + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="deltas", + full_name="spotify.playlist4.proto.ListChanges.deltas", + index=1, + number=2, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="want_resulting_revisions", + full_name= + "spotify.playlist4.proto.ListChanges.want_resulting_revisions", + index=2, + number=3, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="want_sync_result", + full_name="spotify.playlist4.proto.ListChanges.want_sync_result", + index=3, + number=4, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="nonces", + full_name="spotify.playlist4.proto.ListChanges.nonces", + index=4, + number=6, + type=3, + cpp_type=2, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=3040, + serialized_end=3200, +) + +_SELECTEDLISTCONTENT = _descriptor.Descriptor( + name="SelectedListContent", + full_name="spotify.playlist4.proto.SelectedListContent", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="revision", + full_name="spotify.playlist4.proto.SelectedListContent.revision", + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="length", + full_name="spotify.playlist4.proto.SelectedListContent.length", + index=1, + number=2, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="attributes", + full_name="spotify.playlist4.proto.SelectedListContent.attributes", + index=2, + number=3, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="contents", + full_name="spotify.playlist4.proto.SelectedListContent.contents", + index=3, + number=5, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="diff", + full_name="spotify.playlist4.proto.SelectedListContent.diff", + index=4, + number=6, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="sync_result", + full_name="spotify.playlist4.proto.SelectedListContent.sync_result", + index=5, + number=7, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="resulting_revisions", + full_name= + "spotify.playlist4.proto.SelectedListContent.resulting_revisions", + index=6, + number=8, + type=12, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="multiple_heads", + full_name= + "spotify.playlist4.proto.SelectedListContent.multiple_heads", + index=7, + number=9, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="up_to_date", + full_name="spotify.playlist4.proto.SelectedListContent.up_to_date", + index=8, + number=10, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="nonces", + full_name="spotify.playlist4.proto.SelectedListContent.nonces", + index=9, + number=14, + type=3, + cpp_type=2, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="timestamp", + full_name="spotify.playlist4.proto.SelectedListContent.timestamp", + index=10, + number=15, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="owner_username", + full_name= + "spotify.playlist4.proto.SelectedListContent.owner_username", + index=11, + number=16, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=3203, + serialized_end=3602, +) + +_CREATELISTREPLY = _descriptor.Descriptor( + name="CreateListReply", + full_name="spotify.playlist4.proto.CreateListReply", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="uri", + full_name="spotify.playlist4.proto.CreateListReply.uri", + index=0, + number=1, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="revision", + full_name="spotify.playlist4.proto.CreateListReply.revision", + index=1, + number=2, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=3604, + serialized_end=3652, +) + +_MODIFYREPLY = _descriptor.Descriptor( + name="ModifyReply", + full_name="spotify.playlist4.proto.ModifyReply", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="uri", + full_name="spotify.playlist4.proto.ModifyReply.uri", + index=0, + number=1, + type=12, + cpp_type=9, + label=2, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="revision", + full_name="spotify.playlist4.proto.ModifyReply.revision", + index=1, + number=2, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=3654, + serialized_end=3698, +) + +_SUBSCRIBEREQUEST = _descriptor.Descriptor( + name="SubscribeRequest", + full_name="spotify.playlist4.proto.SubscribeRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="uris", + full_name="spotify.playlist4.proto.SubscribeRequest.uris", + index=0, + number=1, + type=12, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=3700, + serialized_end=3732, +) + +_UNSUBSCRIBEREQUEST = _descriptor.Descriptor( + name="UnsubscribeRequest", + full_name="spotify.playlist4.proto.UnsubscribeRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="uris", + full_name="spotify.playlist4.proto.UnsubscribeRequest.uris", + index=0, + number=1, + type=12, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=3734, + serialized_end=3768, +) + +_PLAYLISTMODIFICATIONINFO = _descriptor.Descriptor( + name="PlaylistModificationInfo", + full_name="spotify.playlist4.proto.PlaylistModificationInfo", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="uri", + full_name="spotify.playlist4.proto.PlaylistModificationInfo.uri", + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="new_revision", + full_name= + "spotify.playlist4.proto.PlaylistModificationInfo.new_revision", + index=1, + number=2, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="parent_revision", + full_name= + "spotify.playlist4.proto.PlaylistModificationInfo.parent_revision", + index=2, + number=3, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="ops", + full_name="spotify.playlist4.proto.PlaylistModificationInfo.ops", + index=3, + number=4, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=3771, + serialized_end=3899, +) + +_ITEM.fields_by_name["attributes"].message_type = _ITEMATTRIBUTES +_METAITEM.fields_by_name["attributes"].message_type = _LISTATTRIBUTES +_LISTITEMS.fields_by_name["items"].message_type = _ITEM +_LISTITEMS.fields_by_name["meta_items"].message_type = _METAITEM +_LISTATTRIBUTES.fields_by_name[ + "format_attributes"].message_type = _FORMATLISTATTRIBUTE +_ITEMATTRIBUTES.fields_by_name[ + "format_attributes"].message_type = _FORMATLISTATTRIBUTE +_ADD.fields_by_name["items"].message_type = _ITEM +_REM.fields_by_name["items"].message_type = _ITEM +_ITEMATTRIBUTESPARTIALSTATE.fields_by_name[ + "values"].message_type = _ITEMATTRIBUTES +_ITEMATTRIBUTESPARTIALSTATE.fields_by_name[ + "no_value"].enum_type = _ITEMATTRIBUTEKIND +_LISTATTRIBUTESPARTIALSTATE.fields_by_name[ + "values"].message_type = _LISTATTRIBUTES +_LISTATTRIBUTESPARTIALSTATE.fields_by_name[ + "no_value"].enum_type = _LISTATTRIBUTEKIND +_UPDATEITEMATTRIBUTES.fields_by_name[ + "new_attributes"].message_type = _ITEMATTRIBUTESPARTIALSTATE +_UPDATEITEMATTRIBUTES.fields_by_name[ + "old_attributes"].message_type = _ITEMATTRIBUTESPARTIALSTATE +_UPDATELISTATTRIBUTES.fields_by_name[ + "new_attributes"].message_type = _LISTATTRIBUTESPARTIALSTATE +_UPDATELISTATTRIBUTES.fields_by_name[ + "old_attributes"].message_type = _LISTATTRIBUTESPARTIALSTATE +_OP.fields_by_name["kind"].enum_type = _OP_KIND +_OP.fields_by_name["add"].message_type = _ADD +_OP.fields_by_name["rem"].message_type = _REM +_OP.fields_by_name["mov"].message_type = _MOV +_OP.fields_by_name[ + "update_item_attributes"].message_type = _UPDATEITEMATTRIBUTES +_OP.fields_by_name[ + "update_list_attributes"].message_type = _UPDATELISTATTRIBUTES +_OP_KIND.containing_type = _OP +_OPLIST.fields_by_name["ops"].message_type = _OP +_CHANGEINFO.fields_by_name["source"].message_type = _SOURCEINFO +_SOURCEINFO.fields_by_name["client"].enum_type = _SOURCEINFO_CLIENT +_SOURCEINFO_CLIENT.containing_type = _SOURCEINFO +_DELTA.fields_by_name["ops"].message_type = _OP +_DELTA.fields_by_name["info"].message_type = _CHANGEINFO +_DIFF.fields_by_name["ops"].message_type = _OP +_LISTCHANGES.fields_by_name["deltas"].message_type = _DELTA +_SELECTEDLISTCONTENT.fields_by_name[ + "attributes"].message_type = _LISTATTRIBUTES +_SELECTEDLISTCONTENT.fields_by_name["contents"].message_type = _LISTITEMS +_SELECTEDLISTCONTENT.fields_by_name["diff"].message_type = _DIFF +_SELECTEDLISTCONTENT.fields_by_name["sync_result"].message_type = _DIFF +_PLAYLISTMODIFICATIONINFO.fields_by_name["ops"].message_type = _OP +DESCRIPTOR.message_types_by_name["Item"] = _ITEM +DESCRIPTOR.message_types_by_name["MetaItem"] = _METAITEM +DESCRIPTOR.message_types_by_name["ListItems"] = _LISTITEMS +DESCRIPTOR.message_types_by_name["FormatListAttribute"] = _FORMATLISTATTRIBUTE +DESCRIPTOR.message_types_by_name["ListAttributes"] = _LISTATTRIBUTES +DESCRIPTOR.message_types_by_name["ItemAttributes"] = _ITEMATTRIBUTES +DESCRIPTOR.message_types_by_name["Add"] = _ADD +DESCRIPTOR.message_types_by_name["Rem"] = _REM +DESCRIPTOR.message_types_by_name["Mov"] = _MOV +DESCRIPTOR.message_types_by_name[ + "ItemAttributesPartialState"] = _ITEMATTRIBUTESPARTIALSTATE +DESCRIPTOR.message_types_by_name[ + "ListAttributesPartialState"] = _LISTATTRIBUTESPARTIALSTATE +DESCRIPTOR.message_types_by_name[ + "UpdateItemAttributes"] = _UPDATEITEMATTRIBUTES +DESCRIPTOR.message_types_by_name[ + "UpdateListAttributes"] = _UPDATELISTATTRIBUTES +DESCRIPTOR.message_types_by_name["Op"] = _OP +DESCRIPTOR.message_types_by_name["OpList"] = _OPLIST +DESCRIPTOR.message_types_by_name["ChangeInfo"] = _CHANGEINFO +DESCRIPTOR.message_types_by_name["SourceInfo"] = _SOURCEINFO +DESCRIPTOR.message_types_by_name["Delta"] = _DELTA +DESCRIPTOR.message_types_by_name["Diff"] = _DIFF +DESCRIPTOR.message_types_by_name["ListChanges"] = _LISTCHANGES +DESCRIPTOR.message_types_by_name["SelectedListContent"] = _SELECTEDLISTCONTENT +DESCRIPTOR.message_types_by_name["CreateListReply"] = _CREATELISTREPLY +DESCRIPTOR.message_types_by_name["ModifyReply"] = _MODIFYREPLY +DESCRIPTOR.message_types_by_name["SubscribeRequest"] = _SUBSCRIBEREQUEST +DESCRIPTOR.message_types_by_name["UnsubscribeRequest"] = _UNSUBSCRIBEREQUEST +DESCRIPTOR.message_types_by_name[ + "PlaylistModificationInfo"] = _PLAYLISTMODIFICATIONINFO +DESCRIPTOR.enum_types_by_name["ListAttributeKind"] = _LISTATTRIBUTEKIND +DESCRIPTOR.enum_types_by_name["ItemAttributeKind"] = _ITEMATTRIBUTEKIND +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Item = _reflection.GeneratedProtocolMessageType( + "Item", + (_message.Message, ), + { + "DESCRIPTOR": _ITEM, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.Item) + }, +) +_sym_db.RegisterMessage(Item) + +MetaItem = _reflection.GeneratedProtocolMessageType( + "MetaItem", + (_message.Message, ), + { + "DESCRIPTOR": _METAITEM, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.MetaItem) + }, +) +_sym_db.RegisterMessage(MetaItem) + +ListItems = _reflection.GeneratedProtocolMessageType( + "ListItems", + (_message.Message, ), + { + "DESCRIPTOR": _LISTITEMS, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.ListItems) + }, +) +_sym_db.RegisterMessage(ListItems) + +FormatListAttribute = _reflection.GeneratedProtocolMessageType( + "FormatListAttribute", + (_message.Message, ), + { + "DESCRIPTOR": _FORMATLISTATTRIBUTE, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.FormatListAttribute) + }, +) +_sym_db.RegisterMessage(FormatListAttribute) + +ListAttributes = _reflection.GeneratedProtocolMessageType( + "ListAttributes", + (_message.Message, ), + { + "DESCRIPTOR": _LISTATTRIBUTES, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.ListAttributes) + }, +) +_sym_db.RegisterMessage(ListAttributes) + +ItemAttributes = _reflection.GeneratedProtocolMessageType( + "ItemAttributes", + (_message.Message, ), + { + "DESCRIPTOR": _ITEMATTRIBUTES, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.ItemAttributes) + }, +) +_sym_db.RegisterMessage(ItemAttributes) + +Add = _reflection.GeneratedProtocolMessageType( + "Add", + (_message.Message, ), + { + "DESCRIPTOR": _ADD, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.Add) + }, +) +_sym_db.RegisterMessage(Add) + +Rem = _reflection.GeneratedProtocolMessageType( + "Rem", + (_message.Message, ), + { + "DESCRIPTOR": _REM, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.Rem) + }, +) +_sym_db.RegisterMessage(Rem) + +Mov = _reflection.GeneratedProtocolMessageType( + "Mov", + (_message.Message, ), + { + "DESCRIPTOR": _MOV, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.Mov) + }, +) +_sym_db.RegisterMessage(Mov) + +ItemAttributesPartialState = _reflection.GeneratedProtocolMessageType( + "ItemAttributesPartialState", + (_message.Message, ), + { + "DESCRIPTOR": _ITEMATTRIBUTESPARTIALSTATE, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.ItemAttributesPartialState) + }, +) +_sym_db.RegisterMessage(ItemAttributesPartialState) + +ListAttributesPartialState = _reflection.GeneratedProtocolMessageType( + "ListAttributesPartialState", + (_message.Message, ), + { + "DESCRIPTOR": _LISTATTRIBUTESPARTIALSTATE, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.ListAttributesPartialState) + }, +) +_sym_db.RegisterMessage(ListAttributesPartialState) + +UpdateItemAttributes = _reflection.GeneratedProtocolMessageType( + "UpdateItemAttributes", + (_message.Message, ), + { + "DESCRIPTOR": _UPDATEITEMATTRIBUTES, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.UpdateItemAttributes) + }, +) +_sym_db.RegisterMessage(UpdateItemAttributes) + +UpdateListAttributes = _reflection.GeneratedProtocolMessageType( + "UpdateListAttributes", + (_message.Message, ), + { + "DESCRIPTOR": _UPDATELISTATTRIBUTES, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.UpdateListAttributes) + }, +) +_sym_db.RegisterMessage(UpdateListAttributes) + +Op = _reflection.GeneratedProtocolMessageType( + "Op", + (_message.Message, ), + { + "DESCRIPTOR": _OP, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.Op) + }, +) +_sym_db.RegisterMessage(Op) + +OpList = _reflection.GeneratedProtocolMessageType( + "OpList", + (_message.Message, ), + { + "DESCRIPTOR": _OPLIST, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.OpList) + }, +) +_sym_db.RegisterMessage(OpList) + +ChangeInfo = _reflection.GeneratedProtocolMessageType( + "ChangeInfo", + (_message.Message, ), + { + "DESCRIPTOR": _CHANGEINFO, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.ChangeInfo) + }, +) +_sym_db.RegisterMessage(ChangeInfo) + +SourceInfo = _reflection.GeneratedProtocolMessageType( + "SourceInfo", + (_message.Message, ), + { + "DESCRIPTOR": _SOURCEINFO, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.SourceInfo) + }, +) +_sym_db.RegisterMessage(SourceInfo) + +Delta = _reflection.GeneratedProtocolMessageType( + "Delta", + (_message.Message, ), + { + "DESCRIPTOR": _DELTA, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.Delta) + }, +) +_sym_db.RegisterMessage(Delta) + +Diff = _reflection.GeneratedProtocolMessageType( + "Diff", + (_message.Message, ), + { + "DESCRIPTOR": _DIFF, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.Diff) + }, +) +_sym_db.RegisterMessage(Diff) + +ListChanges = _reflection.GeneratedProtocolMessageType( + "ListChanges", + (_message.Message, ), + { + "DESCRIPTOR": _LISTCHANGES, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.ListChanges) + }, +) +_sym_db.RegisterMessage(ListChanges) + +SelectedListContent = _reflection.GeneratedProtocolMessageType( + "SelectedListContent", + (_message.Message, ), + { + "DESCRIPTOR": _SELECTEDLISTCONTENT, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.SelectedListContent) + }, +) +_sym_db.RegisterMessage(SelectedListContent) + +CreateListReply = _reflection.GeneratedProtocolMessageType( + "CreateListReply", + (_message.Message, ), + { + "DESCRIPTOR": _CREATELISTREPLY, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.CreateListReply) + }, +) +_sym_db.RegisterMessage(CreateListReply) + +ModifyReply = _reflection.GeneratedProtocolMessageType( + "ModifyReply", + (_message.Message, ), + { + "DESCRIPTOR": _MODIFYREPLY, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.ModifyReply) + }, +) +_sym_db.RegisterMessage(ModifyReply) + +SubscribeRequest = _reflection.GeneratedProtocolMessageType( + "SubscribeRequest", + (_message.Message, ), + { + "DESCRIPTOR": _SUBSCRIBEREQUEST, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.SubscribeRequest) + }, +) +_sym_db.RegisterMessage(SubscribeRequest) + +UnsubscribeRequest = _reflection.GeneratedProtocolMessageType( + "UnsubscribeRequest", + (_message.Message, ), + { + "DESCRIPTOR": _UNSUBSCRIBEREQUEST, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.UnsubscribeRequest) + }, +) +_sym_db.RegisterMessage(UnsubscribeRequest) + +PlaylistModificationInfo = _reflection.GeneratedProtocolMessageType( + "PlaylistModificationInfo", + (_message.Message, ), + { + "DESCRIPTOR": _PLAYLISTMODIFICATIONINFO, + "__module__": "playlist4_external_pb2" + # @@protoc_insertion_point(class_scope:spotify.playlist4.proto.PlaylistModificationInfo) + }, +) +_sym_db.RegisterMessage(PlaylistModificationInfo) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/PlaylistAnnotate3_pb2.py b/resources/lib/librespot/proto/PlaylistAnnotate3_pb2.py new file mode 100644 index 0000000..265b99b --- /dev/null +++ b/resources/lib/librespot/proto/PlaylistAnnotate3_pb2.py @@ -0,0 +1,460 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: playlist_annotate3.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import enum_type_wrapper + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="playlist_annotate3.proto", + package="spotify_playlist_annotate3.proto", + syntax="proto2", + serialized_options=b"\n\036com.spotify.playlist_annotate3H\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x18playlist_annotate3.proto\x12 spotify_playlist_annotate3.proto"a\n\x0fTakedownRequest\x12N\n\x12\x61\x62use_report_state\x18\x01 \x01(\x0e\x32\x32.spotify_playlist_annotate3.proto.AbuseReportState"9\n\x0f\x41nnotateRequest\x12\x13\n\x0b\x64\x65scription\x18\x01 \x01(\t\x12\x11\n\timage_uri\x18\x02 \x01(\t"5\n\x11TranscodedPicture\x12\x13\n\x0btarget_name\x18\x01 \x01(\t\x12\x0b\n\x03uri\x18\x02 \x01(\t"\xf4\x02\n\x12PlaylistAnnotation\x12\x13\n\x0b\x64\x65scription\x18\x01 \x01(\t\x12\x0f\n\x07picture\x18\x02 \x01(\t\x12i\n\x1a\x64\x65precated_render_features\x18\x03 \x01(\x0e\x32\x30.spotify_playlist_annotate3.proto.RenderFeatures:\x0fNORMAL_FEATURESB\x02\x18\x01\x12O\n\x12transcoded_picture\x18\x04 \x03(\x0b\x32\x33.spotify_playlist_annotate3.proto.TranscodedPicture\x12(\n\x1ais_abuse_reporting_enabled\x18\x06 \x01(\x08:\x04true\x12R\n\x12\x61\x62use_report_state\x18\x07 \x01(\x0e\x32\x32.spotify_playlist_annotate3.proto.AbuseReportState:\x02OK*<\n\x0eRenderFeatures\x12\x13\n\x0fNORMAL_FEATURES\x10\x01\x12\x15\n\x11\x45XTENDED_FEATURES\x10\x02**\n\x10\x41\x62useReportState\x12\x06\n\x02OK\x10\x00\x12\x0e\n\nTAKEN_DOWN\x10\x01\x42"\n\x1e\x63om.spotify.playlist_annotate3H\x02', +) + +_RENDERFEATURES = _descriptor.EnumDescriptor( + name="RenderFeatures", + full_name="spotify_playlist_annotate3.proto.RenderFeatures", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="NORMAL_FEATURES", + index=0, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="EXTENDED_FEATURES", + index=1, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=650, + serialized_end=710, +) +_sym_db.RegisterEnumDescriptor(_RENDERFEATURES) + +RenderFeatures = enum_type_wrapper.EnumTypeWrapper(_RENDERFEATURES) +_ABUSEREPORTSTATE = _descriptor.EnumDescriptor( + name="AbuseReportState", + full_name="spotify_playlist_annotate3.proto.AbuseReportState", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="OK", + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="TAKEN_DOWN", + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=712, + serialized_end=754, +) +_sym_db.RegisterEnumDescriptor(_ABUSEREPORTSTATE) + +AbuseReportState = enum_type_wrapper.EnumTypeWrapper(_ABUSEREPORTSTATE) +NORMAL_FEATURES = 1 +EXTENDED_FEATURES = 2 +OK = 0 +TAKEN_DOWN = 1 + +_TAKEDOWNREQUEST = _descriptor.Descriptor( + name="TakedownRequest", + full_name="spotify_playlist_annotate3.proto.TakedownRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="abuse_report_state", + full_name= + "spotify_playlist_annotate3.proto.TakedownRequest.abuse_report_state", + index=0, + number=1, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=62, + serialized_end=159, +) + +_ANNOTATEREQUEST = _descriptor.Descriptor( + name="AnnotateRequest", + full_name="spotify_playlist_annotate3.proto.AnnotateRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="description", + full_name= + "spotify_playlist_annotate3.proto.AnnotateRequest.description", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="image_uri", + full_name= + "spotify_playlist_annotate3.proto.AnnotateRequest.image_uri", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=161, + serialized_end=218, +) + +_TRANSCODEDPICTURE = _descriptor.Descriptor( + name="TranscodedPicture", + full_name="spotify_playlist_annotate3.proto.TranscodedPicture", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="target_name", + full_name= + "spotify_playlist_annotate3.proto.TranscodedPicture.target_name", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="uri", + full_name="spotify_playlist_annotate3.proto.TranscodedPicture.uri", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=220, + serialized_end=273, +) + +_PLAYLISTANNOTATION = _descriptor.Descriptor( + name="PlaylistAnnotation", + full_name="spotify_playlist_annotate3.proto.PlaylistAnnotation", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="description", + full_name= + "spotify_playlist_annotate3.proto.PlaylistAnnotation.description", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="picture", + full_name= + "spotify_playlist_annotate3.proto.PlaylistAnnotation.picture", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="deprecated_render_features", + full_name= + "spotify_playlist_annotate3.proto.PlaylistAnnotation.deprecated_render_features", + index=2, + number=3, + type=14, + cpp_type=8, + label=1, + has_default_value=True, + default_value=1, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=b"\030\001", + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="transcoded_picture", + full_name= + "spotify_playlist_annotate3.proto.PlaylistAnnotation.transcoded_picture", + index=3, + number=4, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="is_abuse_reporting_enabled", + full_name= + "spotify_playlist_annotate3.proto.PlaylistAnnotation.is_abuse_reporting_enabled", + index=4, + number=6, + type=8, + cpp_type=7, + label=1, + has_default_value=True, + default_value=True, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="abuse_report_state", + full_name= + "spotify_playlist_annotate3.proto.PlaylistAnnotation.abuse_report_state", + index=5, + number=7, + type=14, + cpp_type=8, + label=1, + has_default_value=True, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=276, + serialized_end=648, +) + +_TAKEDOWNREQUEST.fields_by_name[ + "abuse_report_state"].enum_type = _ABUSEREPORTSTATE +_PLAYLISTANNOTATION.fields_by_name[ + "deprecated_render_features"].enum_type = _RENDERFEATURES +_PLAYLISTANNOTATION.fields_by_name[ + "transcoded_picture"].message_type = _TRANSCODEDPICTURE +_PLAYLISTANNOTATION.fields_by_name[ + "abuse_report_state"].enum_type = _ABUSEREPORTSTATE +DESCRIPTOR.message_types_by_name["TakedownRequest"] = _TAKEDOWNREQUEST +DESCRIPTOR.message_types_by_name["AnnotateRequest"] = _ANNOTATEREQUEST +DESCRIPTOR.message_types_by_name["TranscodedPicture"] = _TRANSCODEDPICTURE +DESCRIPTOR.message_types_by_name["PlaylistAnnotation"] = _PLAYLISTANNOTATION +DESCRIPTOR.enum_types_by_name["RenderFeatures"] = _RENDERFEATURES +DESCRIPTOR.enum_types_by_name["AbuseReportState"] = _ABUSEREPORTSTATE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +TakedownRequest = _reflection.GeneratedProtocolMessageType( + "TakedownRequest", + (_message.Message, ), + { + "DESCRIPTOR": _TAKEDOWNREQUEST, + "__module__": "playlist_annotate3_pb2" + # @@protoc_insertion_point(class_scope:spotify_playlist_annotate3.proto.TakedownRequest) + }, +) +_sym_db.RegisterMessage(TakedownRequest) + +AnnotateRequest = _reflection.GeneratedProtocolMessageType( + "AnnotateRequest", + (_message.Message, ), + { + "DESCRIPTOR": _ANNOTATEREQUEST, + "__module__": "playlist_annotate3_pb2" + # @@protoc_insertion_point(class_scope:spotify_playlist_annotate3.proto.AnnotateRequest) + }, +) +_sym_db.RegisterMessage(AnnotateRequest) + +TranscodedPicture = _reflection.GeneratedProtocolMessageType( + "TranscodedPicture", + (_message.Message, ), + { + "DESCRIPTOR": _TRANSCODEDPICTURE, + "__module__": "playlist_annotate3_pb2" + # @@protoc_insertion_point(class_scope:spotify_playlist_annotate3.proto.TranscodedPicture) + }, +) +_sym_db.RegisterMessage(TranscodedPicture) + +PlaylistAnnotation = _reflection.GeneratedProtocolMessageType( + "PlaylistAnnotation", + (_message.Message, ), + { + "DESCRIPTOR": _PLAYLISTANNOTATION, + "__module__": "playlist_annotate3_pb2" + # @@protoc_insertion_point(class_scope:spotify_playlist_annotate3.proto.PlaylistAnnotation) + }, +) +_sym_db.RegisterMessage(PlaylistAnnotation) + +DESCRIPTOR._options = None +_PLAYLISTANNOTATION.fields_by_name[ + "deprecated_render_features"]._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Pubsub_pb2.py b/resources/lib/librespot/proto/Pubsub_pb2.py new file mode 100644 index 0000000..abe1b3e --- /dev/null +++ b/resources/lib/librespot/proto/Pubsub_pb2.py @@ -0,0 +1,112 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: pubsub.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name='pubsub.proto', + package='spotify', + syntax='proto2', + serialized_options=b'\n\013com.spotify', + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x0cpubsub.proto\x12\x07spotify\"@\n\x0cSubscription\x12\x0b\n\x03uri\x18\x01 \x01(\t\x12\x0e\n\x06\x65xpiry\x18\x02 \x01(\x05\x12\x13\n\x0bstatus_code\x18\x03 \x01(\x05\x42\r\n\x0b\x63om.spotify' +) + +_SUBSCRIPTION = _descriptor.Descriptor( + name='Subscription', + full_name='spotify.Subscription', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='uri', + full_name='spotify.Subscription.uri', + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode('utf-8'), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='expiry', + full_name='spotify.Subscription.expiry', + index=1, + number=2, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='status_code', + full_name='spotify.Subscription.status_code', + index=2, + number=3, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax='proto2', + extension_ranges=[], + oneofs=[], + serialized_start=25, + serialized_end=89, +) + +DESCRIPTOR.message_types_by_name['Subscription'] = _SUBSCRIPTION +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Subscription = _reflection.GeneratedProtocolMessageType( + 'Subscription', + (_message.Message, ), + { + 'DESCRIPTOR': _SUBSCRIPTION, + '__module__': 'pubsub_pb2' + # @@protoc_insertion_point(class_scope:spotify.Subscription) + }) +_sym_db.RegisterMessage(Subscription) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Queue_pb2.py b/resources/lib/librespot/proto/Queue_pb2.py new file mode 100644 index 0000000..0abb762 --- /dev/null +++ b/resources/lib/librespot/proto/Queue_pb2.py @@ -0,0 +1,104 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: queue.proto +"""Generated protocol buffer code.""" +import ContextTrack_pb2 as context__track__pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="queue.proto", + package="spotify.player.proto.transfer", + syntax="proto2", + serialized_options=b"\n\024com.spotify.transferH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x0bqueue.proto\x12\x1dspotify.player.proto.transfer\x1a\x13\x63ontext_track.proto"U\n\x05Queue\x12\x32\n\x06tracks\x18\x01 \x03(\x0b\x32".spotify.player.proto.ContextTrack\x12\x18\n\x10is_playing_queue\x18\x02 \x01(\x08\x42\x18\n\x14\x63om.spotify.transferH\x02', + dependencies=[ + context__track__pb2.DESCRIPTOR, + ], +) + +_QUEUE = _descriptor.Descriptor( + name="Queue", + full_name="spotify.player.proto.transfer.Queue", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="tracks", + full_name="spotify.player.proto.transfer.Queue.tracks", + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="is_playing_queue", + full_name="spotify.player.proto.transfer.Queue.is_playing_queue", + index=1, + number=2, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=67, + serialized_end=152, +) + +_QUEUE.fields_by_name[ + "tracks"].message_type = context__track__pb2._CONTEXTTRACK +DESCRIPTOR.message_types_by_name["Queue"] = _QUEUE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Queue = _reflection.GeneratedProtocolMessageType( + "Queue", + (_message.Message, ), + { + "DESCRIPTOR": _QUEUE, + "__module__": "queue_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.transfer.Queue) + }, +) +_sym_db.RegisterMessage(Queue) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Restrictions_pb2.py b/resources/lib/librespot/proto/Restrictions_pb2.py new file mode 100644 index 0000000..489e934 --- /dev/null +++ b/resources/lib/librespot/proto/Restrictions_pb2.py @@ -0,0 +1,480 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: restrictions.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="restrictions.proto", + package="spotify.player.proto", + syntax="proto2", + serialized_options=b"\n\023com.spotify.contextH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x12restrictions.proto\x12\x14spotify.player.proto"\xbb\x07\n\x0cRestrictions\x12 \n\x18\x64isallow_pausing_reasons\x18\x01 \x03(\t\x12!\n\x19\x64isallow_resuming_reasons\x18\x02 \x03(\t\x12 \n\x18\x64isallow_seeking_reasons\x18\x03 \x03(\t\x12%\n\x1d\x64isallow_peeking_prev_reasons\x18\x04 \x03(\t\x12%\n\x1d\x64isallow_peeking_next_reasons\x18\x05 \x03(\t\x12&\n\x1e\x64isallow_skipping_prev_reasons\x18\x06 \x03(\t\x12&\n\x1e\x64isallow_skipping_next_reasons\x18\x07 \x03(\t\x12\x30\n(disallow_toggling_repeat_context_reasons\x18\x08 \x03(\t\x12.\n&disallow_toggling_repeat_track_reasons\x18\t \x03(\t\x12)\n!disallow_toggling_shuffle_reasons\x18\n \x03(\t\x12"\n\x1a\x64isallow_set_queue_reasons\x18\x0b \x03(\t\x12.\n&disallow_interrupting_playback_reasons\x18\x0c \x03(\t\x12.\n&disallow_transferring_playback_reasons\x18\r \x03(\t\x12\'\n\x1f\x64isallow_remote_control_reasons\x18\x0e \x03(\t\x12\x33\n+disallow_inserting_into_next_tracks_reasons\x18\x0f \x03(\t\x12\x36\n.disallow_inserting_into_context_tracks_reasons\x18\x10 \x03(\t\x12\x32\n*disallow_reordering_in_next_tracks_reasons\x18\x11 \x03(\t\x12\x35\n-disallow_reordering_in_context_tracks_reasons\x18\x12 \x03(\t\x12\x32\n*disallow_removing_from_next_tracks_reasons\x18\x13 \x03(\t\x12\x35\n-disallow_removing_from_context_tracks_reasons\x18\x14 \x03(\t\x12)\n!disallow_updating_context_reasons\x18\x15 \x03(\tB\x17\n\x13\x63om.spotify.contextH\x02', +) + +_RESTRICTIONS = _descriptor.Descriptor( + name="Restrictions", + full_name="spotify.player.proto.Restrictions", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="disallow_pausing_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_pausing_reasons", + index=0, + number=1, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_resuming_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_resuming_reasons", + index=1, + number=2, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_seeking_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_seeking_reasons", + index=2, + number=3, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_peeking_prev_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_peeking_prev_reasons", + index=3, + number=4, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_peeking_next_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_peeking_next_reasons", + index=4, + number=5, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_skipping_prev_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_skipping_prev_reasons", + index=5, + number=6, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_skipping_next_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_skipping_next_reasons", + index=6, + number=7, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_toggling_repeat_context_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_toggling_repeat_context_reasons", + index=7, + number=8, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_toggling_repeat_track_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_toggling_repeat_track_reasons", + index=8, + number=9, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_toggling_shuffle_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_toggling_shuffle_reasons", + index=9, + number=10, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_set_queue_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_set_queue_reasons", + index=10, + number=11, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_interrupting_playback_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_interrupting_playback_reasons", + index=11, + number=12, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_transferring_playback_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_transferring_playback_reasons", + index=12, + number=13, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_remote_control_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_remote_control_reasons", + index=13, + number=14, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_inserting_into_next_tracks_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_inserting_into_next_tracks_reasons", + index=14, + number=15, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_inserting_into_context_tracks_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_inserting_into_context_tracks_reasons", + index=15, + number=16, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_reordering_in_next_tracks_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_reordering_in_next_tracks_reasons", + index=16, + number=17, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_reordering_in_context_tracks_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_reordering_in_context_tracks_reasons", + index=17, + number=18, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_removing_from_next_tracks_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_removing_from_next_tracks_reasons", + index=18, + number=19, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_removing_from_context_tracks_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_removing_from_context_tracks_reasons", + index=19, + number=20, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="disallow_updating_context_reasons", + full_name= + "spotify.player.proto.Restrictions.disallow_updating_context_reasons", + index=20, + number=21, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=45, + serialized_end=1000, +) + +DESCRIPTOR.message_types_by_name["Restrictions"] = _RESTRICTIONS +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Restrictions = _reflection.GeneratedProtocolMessageType( + "Restrictions", + (_message.Message, ), + { + "DESCRIPTOR": _RESTRICTIONS, + "__module__": "restrictions_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.Restrictions) + }, +) +_sym_db.RegisterMessage(Restrictions) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/Session_pb2.py b/resources/lib/librespot/proto/Session_pb2.py new file mode 100644 index 0000000..9214092 --- /dev/null +++ b/resources/lib/librespot/proto/Session_pb2.py @@ -0,0 +1,149 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: session.proto +"""Generated protocol buffer code.""" +import Context_pb2 as context__pb2 +import ContextPlayerOptions_pb2 as context__player__options__pb2 +import PlayOrigin_pb2 as play__origin__pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="session.proto", + package="spotify.player.proto.transfer", + syntax="proto2", + serialized_options=b"\n\024com.spotify.transferH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\rsession.proto\x12\x1dspotify.player.proto.transfer\x1a\rcontext.proto\x1a\x1c\x63ontext_player_options.proto\x1a\x11play_origin.proto"\xd3\x01\n\x07Session\x12\x35\n\x0bplay_origin\x18\x01 \x01(\x0b\x32 .spotify.player.proto.PlayOrigin\x12.\n\x07\x63ontext\x18\x02 \x01(\x0b\x32\x1d.spotify.player.proto.Context\x12\x13\n\x0b\x63urrent_uid\x18\x03 \x01(\t\x12L\n\x10option_overrides\x18\x04 \x01(\x0b\x32\x32.spotify.player.proto.ContextPlayerOptionOverridesB\x18\n\x14\x63om.spotify.transferH\x02', + dependencies=[ + context__pb2.DESCRIPTOR, + context__player__options__pb2.DESCRIPTOR, + play__origin__pb2.DESCRIPTOR, + ], +) + +_SESSION = _descriptor.Descriptor( + name="Session", + full_name="spotify.player.proto.transfer.Session", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="play_origin", + full_name="spotify.player.proto.transfer.Session.play_origin", + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="context", + full_name="spotify.player.proto.transfer.Session.context", + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="current_uid", + full_name="spotify.player.proto.transfer.Session.current_uid", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="option_overrides", + full_name="spotify.player.proto.transfer.Session.option_overrides", + index=3, + number=4, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=113, + serialized_end=324, +) + +_SESSION.fields_by_name[ + "play_origin"].message_type = play__origin__pb2._PLAYORIGIN +_SESSION.fields_by_name["context"].message_type = context__pb2._CONTEXT +_SESSION.fields_by_name[ + "option_overrides"].message_type = context__player__options__pb2._CONTEXTPLAYEROPTIONOVERRIDES +DESCRIPTOR.message_types_by_name["Session"] = _SESSION +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Session = _reflection.GeneratedProtocolMessageType( + "Session", + (_message.Message, ), + { + "DESCRIPTOR": _SESSION, + "__module__": "session_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.transfer.Session) + }, +) +_sym_db.RegisterMessage(Session) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/StorageResolve_pb2.py b/resources/lib/librespot/proto/StorageResolve_pb2.py new file mode 100644 index 0000000..3319832 --- /dev/null +++ b/resources/lib/librespot/proto/StorageResolve_pb2.py @@ -0,0 +1,154 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: storage-resolve.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name='storage-resolve.proto', + package='spotify.download.proto', + syntax='proto3', + serialized_options=b'\n\023com.spotify.storageH\002', + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x15storage-resolve.proto\x12\x16spotify.download.proto\"\xaf\x01\n\x16StorageResolveResponse\x12\x45\n\x06result\x18\x01 \x01(\x0e\x32\x35.spotify.download.proto.StorageResolveResponse.Result\x12\x0e\n\x06\x63\x64nurl\x18\x02 \x03(\t\x12\x0e\n\x06\x66ileid\x18\x04 \x01(\x0c\".\n\x06Result\x12\x07\n\x03\x43\x44N\x10\x00\x12\x0b\n\x07STORAGE\x10\x01\x12\x0e\n\nRESTRICTED\x10\x03\x42\x17\n\x13\x63om.spotify.storageH\x02\x62\x06proto3' +) + +_STORAGERESOLVERESPONSE_RESULT = _descriptor.EnumDescriptor( + name='Result', + full_name='spotify.download.proto.StorageResolveResponse.Result', + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name='CDN', + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='STORAGE', + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + _descriptor.EnumValueDescriptor( + name='RESTRICTED', + index=2, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key), + ], + containing_type=None, + serialized_options=None, + serialized_start=179, + serialized_end=225, +) +_sym_db.RegisterEnumDescriptor(_STORAGERESOLVERESPONSE_RESULT) + +_STORAGERESOLVERESPONSE = _descriptor.Descriptor( + name='StorageResolveResponse', + full_name='spotify.download.proto.StorageResolveResponse', + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name='result', + full_name='spotify.download.proto.StorageResolveResponse.result', + index=0, + number=1, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='cdnurl', + full_name='spotify.download.proto.StorageResolveResponse.cdnurl', + index=1, + number=2, + type=9, + cpp_type=9, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + _descriptor.FieldDescriptor( + name='fileid', + full_name='spotify.download.proto.StorageResolveResponse.fileid', + index=2, + number=4, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key), + ], + extensions=[], + nested_types=[], + enum_types=[ + _STORAGERESOLVERESPONSE_RESULT, + ], + serialized_options=None, + is_extendable=False, + syntax='proto3', + extension_ranges=[], + oneofs=[], + serialized_start=50, + serialized_end=225, +) + +_STORAGERESOLVERESPONSE.fields_by_name[ + 'result'].enum_type = _STORAGERESOLVERESPONSE_RESULT +_STORAGERESOLVERESPONSE_RESULT.containing_type = _STORAGERESOLVERESPONSE +DESCRIPTOR.message_types_by_name[ + 'StorageResolveResponse'] = _STORAGERESOLVERESPONSE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +StorageResolveResponse = _reflection.GeneratedProtocolMessageType( + 'StorageResolveResponse', + (_message.Message, ), + { + 'DESCRIPTOR': _STORAGERESOLVERESPONSE, + '__module__': 'storage_resolve_pb2' + # @@protoc_insertion_point(class_scope:spotify.download.proto.StorageResolveResponse) + }) +_sym_db.RegisterMessage(StorageResolveResponse) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/TransferState_pb2.py b/resources/lib/librespot/proto/TransferState_pb2.py new file mode 100644 index 0000000..1db7786 --- /dev/null +++ b/resources/lib/librespot/proto/TransferState_pb2.py @@ -0,0 +1,174 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: transfer_state.proto +"""Generated protocol buffer code.""" +import ContextPlayerOptions_pb2 as context__player__options__pb2 +import Playback_pb2 as playback__pb2 +import Queue_pb2 as queue__pb2 +import Session_pb2 as session__pb2 +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="transfer_state.proto", + package="spotify.player.proto.transfer", + syntax="proto2", + serialized_options=b"\n\024com.spotify.transferH\002", + create_key=_descriptor._internal_create_key, + serialized_pb= + b"\n\x14transfer_state.proto\x12\x1dspotify.player.proto.transfer\x1a\x1c\x63ontext_player_options.proto\x1a\x0eplayback.proto\x1a\rsession.proto\x1a\x0bqueue.proto\"\x99\x02\n\rTransferState\x12;\n\x07options\x18\x01 \x01(\x0b\x32*.spotify.player.proto.ContextPlayerOptions\x12\x39\n\x08playback\x18\x02 \x01(\x0b\x32'.spotify.player.proto.transfer.Playback\x12?\n\x0f\x63urrent_session\x18\x03 \x01(\x0b\x32&.spotify.player.proto.transfer.Session\x12\x33\n\x05queue\x18\x04 \x01(\x0b\x32$.spotify.player.proto.transfer.Queue\x12\x1a\n\x12\x63reation_timestamp\x18\x05 \x01(\x03\x42\x18\n\x14\x63om.spotify.transferH\x02", + dependencies=[ + context__player__options__pb2.DESCRIPTOR, + playback__pb2.DESCRIPTOR, + session__pb2.DESCRIPTOR, + queue__pb2.DESCRIPTOR, + ], +) + +_TRANSFERSTATE = _descriptor.Descriptor( + name="TransferState", + full_name="spotify.player.proto.transfer.TransferState", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="options", + full_name="spotify.player.proto.transfer.TransferState.options", + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="playback", + full_name="spotify.player.proto.transfer.TransferState.playback", + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="current_session", + full_name= + "spotify.player.proto.transfer.TransferState.current_session", + index=2, + number=3, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="queue", + full_name="spotify.player.proto.transfer.TransferState.queue", + index=3, + number=4, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="creation_timestamp", + full_name= + "spotify.player.proto.transfer.TransferState.creation_timestamp", + index=4, + number=5, + type=3, + cpp_type=2, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto2", + extension_ranges=[], + oneofs=[], + serialized_start=130, + serialized_end=411, +) + +_TRANSFERSTATE.fields_by_name[ + "options"].message_type = context__player__options__pb2._CONTEXTPLAYEROPTIONS +_TRANSFERSTATE.fields_by_name[ + "playback"].message_type = playback__pb2._PLAYBACK +_TRANSFERSTATE.fields_by_name[ + "current_session"].message_type = session__pb2._SESSION +_TRANSFERSTATE.fields_by_name["queue"].message_type = queue__pb2._QUEUE +DESCRIPTOR.message_types_by_name["TransferState"] = _TRANSFERSTATE +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +TransferState = _reflection.GeneratedProtocolMessageType( + "TransferState", + (_message.Message, ), + { + "DESCRIPTOR": _TRANSFERSTATE, + "__module__": "transfer_state_pb2" + # @@protoc_insertion_point(class_scope:spotify.player.proto.transfer.TransferState) + }, +) +_sym_db.RegisterMessage(TransferState) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/__init__.py b/resources/lib/librespot/proto/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/resources/lib/librespot/proto/spotify/__init__.py b/resources/lib/librespot/proto/spotify/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/resources/lib/librespot/proto/spotify/login5/__init__.py b/resources/lib/librespot/proto/spotify/login5/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/resources/lib/librespot/proto/spotify/login5/v3/ClientInfo_pb2.py b/resources/lib/librespot/proto/spotify/login5/v3/ClientInfo_pb2.py new file mode 100644 index 0000000..81fc083 --- /dev/null +++ b/resources/lib/librespot/proto/spotify/login5/v3/ClientInfo_pb2.py @@ -0,0 +1,98 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: spotify/login5/v3/client_info.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="spotify/login5/v3/client_info.proto", + package="spotify.login5.v3", + syntax="proto3", + serialized_options=b"\n\024com.spotify.login5v3", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n#spotify/login5/v3/client_info.proto\x12\x11spotify.login5.v3"2\n\nClientInfo\x12\x11\n\tclient_id\x18\x01 \x01(\t\x12\x11\n\tdevice_id\x18\x02 \x01(\tB\x16\n\x14\x63om.spotify.login5v3b\x06proto3', +) + +_CLIENTINFO = _descriptor.Descriptor( + name="ClientInfo", + full_name="spotify.login5.v3.ClientInfo", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="client_id", + full_name="spotify.login5.v3.ClientInfo.client_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="device_id", + full_name="spotify.login5.v3.ClientInfo.device_id", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=58, + serialized_end=108, +) + +DESCRIPTOR.message_types_by_name["ClientInfo"] = _CLIENTINFO +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +ClientInfo = _reflection.GeneratedProtocolMessageType( + "ClientInfo", + (_message.Message, ), + { + "DESCRIPTOR": _CLIENTINFO, + "__module__": "spotify.login5.v3.client_info_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.ClientInfo) + }, +) +_sym_db.RegisterMessage(ClientInfo) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/spotify/login5/v3/Login5_pb2.py b/resources/lib/librespot/proto/spotify/login5/v3/Login5_pb2.py new file mode 100644 index 0000000..566aca9 --- /dev/null +++ b/resources/lib/librespot/proto/spotify/login5/v3/Login5_pb2.py @@ -0,0 +1,972 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: spotify/login5/v3/login5.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database +from google.protobuf.internal import enum_type_wrapper +from spotify.login5.v3 import \ + client_info_pb2 as spotify_dot_login5_dot_v3_dot_client__info__pb2 +from spotify.login5.v3 import \ + user_info_pb2 as spotify_dot_login5_dot_v3_dot_user__info__pb2 +from spotify.login5.v3.challenges import \ + code_pb2 as spotify_dot_login5_dot_v3_dot_challenges_dot_code__pb2 +from spotify.login5.v3.challenges import \ + hashcash_pb2 as spotify_dot_login5_dot_v3_dot_challenges_dot_hashcash__pb2 +from spotify.login5.v3.credentials import \ + credentials_pb2 as \ + spotify_dot_login5_dot_v3_dot_credentials_dot_credentials__pb2 +from spotify.login5.v3.identifiers import \ + identifiers_pb2 as \ + spotify_dot_login5_dot_v3_dot_identifiers_dot_identifiers__pb2 + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="spotify/login5/v3/login5.proto", + package="spotify.login5.v3", + syntax="proto3", + serialized_options=b"\n\024com.spotify.login5v3", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\x1espotify/login5/v3/login5.proto\x12\x11spotify.login5.v3\x1a#spotify/login5/v3/client_info.proto\x1a!spotify/login5/v3/user_info.proto\x1a\'spotify/login5/v3/challenges/code.proto\x1a+spotify/login5/v3/challenges/hashcash.proto\x1a/spotify/login5/v3/credentials/credentials.proto\x1a/spotify/login5/v3/identifiers/identifiers.proto">\n\nChallenges\x12\x30\n\nchallenges\x18\x01 \x03(\x0b\x32\x1c.spotify.login5.v3.Challenge"\x89\x01\n\tChallenge\x12\x41\n\x08hashcash\x18\x01 \x01(\x0b\x32/.spotify.login5.v3.challenges.HashcashChallenge\x12\x39\n\x04\x63ode\x18\x02 \x01(\x0b\x32+.spotify.login5.v3.challenges.CodeChallenge"M\n\x12\x43hallengeSolutions\x12\x37\n\tsolutions\x18\x01 \x03(\x0b\x32$.spotify.login5.v3.ChallengeSolution"\x8f\x01\n\x11\x43hallengeSolution\x12@\n\x08hashcash\x18\x01 \x01(\x0b\x32..spotify.login5.v3.challenges.HashcashSolution\x12\x38\n\x04\x63ode\x18\x02 \x01(\x0b\x32*.spotify.login5.v3.challenges.CodeSolution"\xad\x05\n\x0cLoginRequest\x12\x32\n\x0b\x63lient_info\x18\x01 \x01(\x0b\x32\x1d.spotify.login5.v3.ClientInfo\x12\x15\n\rlogin_context\x18\x02 \x01(\x0c\x12\x42\n\x13\x63hallenge_solutions\x18\x03 \x01(\x0b\x32%.spotify.login5.v3.ChallengeSolutions\x12J\n\x11stored_credential\x18\x64 \x01(\x0b\x32/.spotify.login5.v3.credentials.StoredCredential\x12\x39\n\x08password\x18\x65 \x01(\x0b\x32\'.spotify.login5.v3.credentials.Password\x12Q\n\x15\x66\x61\x63\x65\x62ook_access_token\x18\x66 \x01(\x0b\x32\x32.spotify.login5.v3.credentials.FacebookAccessToken\x12@\n\x0cphone_number\x18g \x01(\x0b\x32*.spotify.login5.v3.identifiers.PhoneNumber\x12\x43\n\x0eone_time_token\x18h \x01(\x0b\x32+.spotify.login5.v3.credentials.OneTimeToken\x12U\n\x17parent_child_credential\x18i \x01(\x0b\x32\x34.spotify.login5.v3.credentials.ParentChildCredential\x12V\n\x18\x61pple_sign_in_credential\x18j \x01(\x0b\x32\x34.spotify.login5.v3.credentials.AppleSignInCredential"m\n\x07LoginOk\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x02 \x01(\t\x12\x19\n\x11stored_credential\x18\x03 \x01(\x0c\x12\x1f\n\x17\x61\x63\x63\x65ss_token_expires_in\x18\x04 \x01(\x05"\xf8\x02\n\rLoginResponse\x12&\n\x02ok\x18\x01 \x01(\x0b\x32\x1a.spotify.login5.v3.LoginOk\x12,\n\x05\x65rror\x18\x02 \x01(\x0e\x32\x1d.spotify.login5.v3.LoginError\x12\x31\n\nchallenges\x18\x03 \x01(\x0b\x32\x1d.spotify.login5.v3.Challenges\x12;\n\x08warnings\x18\x04 \x03(\x0e\x32).spotify.login5.v3.LoginResponse.Warnings\x12\x15\n\rlogin_context\x18\x05 \x01(\x0c\x12\x18\n\x10identifier_token\x18\x06 \x01(\t\x12.\n\tuser_info\x18\x07 \x01(\x0b\x32\x1b.spotify.login5.v3.UserInfo"@\n\x08Warnings\x12\x13\n\x0fUNKNOWN_WARNING\x10\x00\x12\x1f\n\x1b\x44\x45PRECATED_PROTOCOL_VERSION\x10\x01*\xd3\x01\n\nLoginError\x12\x11\n\rUNKNOWN_ERROR\x10\x00\x12\x17\n\x13INVALID_CREDENTIALS\x10\x01\x12\x0f\n\x0b\x42\x41\x44_REQUEST\x10\x02\x12\x1e\n\x1aUNSUPPORTED_LOGIN_PROTOCOL\x10\x03\x12\x0b\n\x07TIMEOUT\x10\x04\x12\x16\n\x12UNKNOWN_IDENTIFIER\x10\x05\x12\x15\n\x11TOO_MANY_ATTEMPTS\x10\x06\x12\x17\n\x13INVALID_PHONENUMBER\x10\x07\x12\x13\n\x0fTRY_AGAIN_LATER\x10\x08\x42\x16\n\x14\x63om.spotify.login5v3b\x06proto3', + dependencies=[ + spotify_dot_login5_dot_v3_dot_client__info__pb2.DESCRIPTOR, + spotify_dot_login5_dot_v3_dot_user__info__pb2.DESCRIPTOR, + spotify_dot_login5_dot_v3_dot_challenges_dot_code__pb2.DESCRIPTOR, + spotify_dot_login5_dot_v3_dot_challenges_dot_hashcash__pb2.DESCRIPTOR, + spotify_dot_login5_dot_v3_dot_credentials_dot_credentials__pb2. + DESCRIPTOR, + spotify_dot_login5_dot_v3_dot_identifiers_dot_identifiers__pb2. + DESCRIPTOR, + ], +) + +_LOGINERROR = _descriptor.EnumDescriptor( + name="LoginError", + full_name="spotify.login5.v3.LoginError", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="UNKNOWN_ERROR", + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="INVALID_CREDENTIALS", + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="BAD_REQUEST", + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="UNSUPPORTED_LOGIN_PROTOCOL", + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="TIMEOUT", + index=4, + number=4, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="UNKNOWN_IDENTIFIER", + index=5, + number=5, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="TOO_MANY_ATTEMPTS", + index=6, + number=6, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="INVALID_PHONENUMBER", + index=7, + number=7, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="TRY_AGAIN_LATER", + index=8, + number=8, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=1917, + serialized_end=2128, +) +_sym_db.RegisterEnumDescriptor(_LOGINERROR) + +LoginError = enum_type_wrapper.EnumTypeWrapper(_LOGINERROR) +UNKNOWN_ERROR = 0 +INVALID_CREDENTIALS = 1 +BAD_REQUEST = 2 +UNSUPPORTED_LOGIN_PROTOCOL = 3 +TIMEOUT = 4 +UNKNOWN_IDENTIFIER = 5 +TOO_MANY_ATTEMPTS = 6 +INVALID_PHONENUMBER = 7 +TRY_AGAIN_LATER = 8 + +_LOGINRESPONSE_WARNINGS = _descriptor.EnumDescriptor( + name="Warnings", + full_name="spotify.login5.v3.LoginResponse.Warnings", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="UNKNOWN_WARNING", + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="DEPRECATED_PROTOCOL_VERSION", + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=1850, + serialized_end=1914, +) +_sym_db.RegisterEnumDescriptor(_LOGINRESPONSE_WARNINGS) + +_CHALLENGES = _descriptor.Descriptor( + name="Challenges", + full_name="spotify.login5.v3.Challenges", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="challenges", + full_name="spotify.login5.v3.Challenges.challenges", + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=309, + serialized_end=371, +) + +_CHALLENGE = _descriptor.Descriptor( + name="Challenge", + full_name="spotify.login5.v3.Challenge", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="hashcash", + full_name="spotify.login5.v3.Challenge.hashcash", + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="code", + full_name="spotify.login5.v3.Challenge.code", + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=374, + serialized_end=511, +) + +_CHALLENGESOLUTIONS = _descriptor.Descriptor( + name="ChallengeSolutions", + full_name="spotify.login5.v3.ChallengeSolutions", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="solutions", + full_name="spotify.login5.v3.ChallengeSolutions.solutions", + index=0, + number=1, + type=11, + cpp_type=10, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=513, + serialized_end=590, +) + +_CHALLENGESOLUTION = _descriptor.Descriptor( + name="ChallengeSolution", + full_name="spotify.login5.v3.ChallengeSolution", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="hashcash", + full_name="spotify.login5.v3.ChallengeSolution.hashcash", + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="code", + full_name="spotify.login5.v3.ChallengeSolution.code", + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=593, + serialized_end=736, +) + +_LOGINREQUEST = _descriptor.Descriptor( + name="LoginRequest", + full_name="spotify.login5.v3.LoginRequest", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="client_info", + full_name="spotify.login5.v3.LoginRequest.client_info", + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="login_context", + full_name="spotify.login5.v3.LoginRequest.login_context", + index=1, + number=2, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="challenge_solutions", + full_name="spotify.login5.v3.LoginRequest.challenge_solutions", + index=2, + number=3, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="stored_credential", + full_name="spotify.login5.v3.LoginRequest.stored_credential", + index=3, + number=100, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="password", + full_name="spotify.login5.v3.LoginRequest.password", + index=4, + number=101, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="facebook_access_token", + full_name="spotify.login5.v3.LoginRequest.facebook_access_token", + index=5, + number=102, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="phone_number", + full_name="spotify.login5.v3.LoginRequest.phone_number", + index=6, + number=103, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="one_time_token", + full_name="spotify.login5.v3.LoginRequest.one_time_token", + index=7, + number=104, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="parent_child_credential", + full_name="spotify.login5.v3.LoginRequest.parent_child_credential", + index=8, + number=105, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="apple_sign_in_credential", + full_name="spotify.login5.v3.LoginRequest.apple_sign_in_credential", + index=9, + number=106, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=739, + serialized_end=1424, +) + +_LOGINOK = _descriptor.Descriptor( + name="LoginOk", + full_name="spotify.login5.v3.LoginOk", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="username", + full_name="spotify.login5.v3.LoginOk.username", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="access_token", + full_name="spotify.login5.v3.LoginOk.access_token", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="stored_credential", + full_name="spotify.login5.v3.LoginOk.stored_credential", + index=2, + number=3, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="access_token_expires_in", + full_name="spotify.login5.v3.LoginOk.access_token_expires_in", + index=3, + number=4, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1426, + serialized_end=1535, +) + +_LOGINRESPONSE = _descriptor.Descriptor( + name="LoginResponse", + full_name="spotify.login5.v3.LoginResponse", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="ok", + full_name="spotify.login5.v3.LoginResponse.ok", + index=0, + number=1, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="error", + full_name="spotify.login5.v3.LoginResponse.error", + index=1, + number=2, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="challenges", + full_name="spotify.login5.v3.LoginResponse.challenges", + index=2, + number=3, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="warnings", + full_name="spotify.login5.v3.LoginResponse.warnings", + index=3, + number=4, + type=14, + cpp_type=8, + label=3, + has_default_value=False, + default_value=[], + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="login_context", + full_name="spotify.login5.v3.LoginResponse.login_context", + index=4, + number=5, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="identifier_token", + full_name="spotify.login5.v3.LoginResponse.identifier_token", + index=5, + number=6, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="user_info", + full_name="spotify.login5.v3.LoginResponse.user_info", + index=6, + number=7, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[ + _LOGINRESPONSE_WARNINGS, + ], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=1538, + serialized_end=1914, +) + +_CHALLENGES.fields_by_name["challenges"].message_type = _CHALLENGE +_CHALLENGE.fields_by_name["hashcash"].message_type = ( + spotify_dot_login5_dot_v3_dot_challenges_dot_hashcash__pb2. + _HASHCASHCHALLENGE) +_CHALLENGE.fields_by_name[ + "code"].message_type = spotify_dot_login5_dot_v3_dot_challenges_dot_code__pb2._CODECHALLENGE +_CHALLENGESOLUTIONS.fields_by_name[ + "solutions"].message_type = _CHALLENGESOLUTION +_CHALLENGESOLUTION.fields_by_name["hashcash"].message_type = ( + spotify_dot_login5_dot_v3_dot_challenges_dot_hashcash__pb2. + _HASHCASHSOLUTION) +_CHALLENGESOLUTION.fields_by_name[ + "code"].message_type = spotify_dot_login5_dot_v3_dot_challenges_dot_code__pb2._CODESOLUTION +_LOGINREQUEST.fields_by_name[ + "client_info"].message_type = spotify_dot_login5_dot_v3_dot_client__info__pb2._CLIENTINFO +_LOGINREQUEST.fields_by_name[ + "challenge_solutions"].message_type = _CHALLENGESOLUTIONS +_LOGINREQUEST.fields_by_name["stored_credential"].message_type = ( + spotify_dot_login5_dot_v3_dot_credentials_dot_credentials__pb2. + _STOREDCREDENTIAL) +_LOGINREQUEST.fields_by_name["password"].message_type = ( + spotify_dot_login5_dot_v3_dot_credentials_dot_credentials__pb2._PASSWORD) +_LOGINREQUEST.fields_by_name["facebook_access_token"].message_type = ( + spotify_dot_login5_dot_v3_dot_credentials_dot_credentials__pb2. + _FACEBOOKACCESSTOKEN) +_LOGINREQUEST.fields_by_name["phone_number"].message_type = ( + spotify_dot_login5_dot_v3_dot_identifiers_dot_identifiers__pb2._PHONENUMBER +) +_LOGINREQUEST.fields_by_name["one_time_token"].message_type = ( + spotify_dot_login5_dot_v3_dot_credentials_dot_credentials__pb2. + _ONETIMETOKEN) +_LOGINREQUEST.fields_by_name["parent_child_credential"].message_type = ( + spotify_dot_login5_dot_v3_dot_credentials_dot_credentials__pb2. + _PARENTCHILDCREDENTIAL) +_LOGINREQUEST.fields_by_name["apple_sign_in_credential"].message_type = ( + spotify_dot_login5_dot_v3_dot_credentials_dot_credentials__pb2. + _APPLESIGNINCREDENTIAL) +_LOGINRESPONSE.fields_by_name["ok"].message_type = _LOGINOK +_LOGINRESPONSE.fields_by_name["error"].enum_type = _LOGINERROR +_LOGINRESPONSE.fields_by_name["challenges"].message_type = _CHALLENGES +_LOGINRESPONSE.fields_by_name["warnings"].enum_type = _LOGINRESPONSE_WARNINGS +_LOGINRESPONSE.fields_by_name[ + "user_info"].message_type = spotify_dot_login5_dot_v3_dot_user__info__pb2._USERINFO +_LOGINRESPONSE_WARNINGS.containing_type = _LOGINRESPONSE +DESCRIPTOR.message_types_by_name["Challenges"] = _CHALLENGES +DESCRIPTOR.message_types_by_name["Challenge"] = _CHALLENGE +DESCRIPTOR.message_types_by_name["ChallengeSolutions"] = _CHALLENGESOLUTIONS +DESCRIPTOR.message_types_by_name["ChallengeSolution"] = _CHALLENGESOLUTION +DESCRIPTOR.message_types_by_name["LoginRequest"] = _LOGINREQUEST +DESCRIPTOR.message_types_by_name["LoginOk"] = _LOGINOK +DESCRIPTOR.message_types_by_name["LoginResponse"] = _LOGINRESPONSE +DESCRIPTOR.enum_types_by_name["LoginError"] = _LOGINERROR +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +Challenges = _reflection.GeneratedProtocolMessageType( + "Challenges", + (_message.Message, ), + { + "DESCRIPTOR": _CHALLENGES, + "__module__": "spotify.login5.v3.login5_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.Challenges) + }, +) +_sym_db.RegisterMessage(Challenges) + +Challenge = _reflection.GeneratedProtocolMessageType( + "Challenge", + (_message.Message, ), + { + "DESCRIPTOR": _CHALLENGE, + "__module__": "spotify.login5.v3.login5_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.Challenge) + }, +) +_sym_db.RegisterMessage(Challenge) + +ChallengeSolutions = _reflection.GeneratedProtocolMessageType( + "ChallengeSolutions", + (_message.Message, ), + { + "DESCRIPTOR": _CHALLENGESOLUTIONS, + "__module__": "spotify.login5.v3.login5_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.ChallengeSolutions) + }, +) +_sym_db.RegisterMessage(ChallengeSolutions) + +ChallengeSolution = _reflection.GeneratedProtocolMessageType( + "ChallengeSolution", + (_message.Message, ), + { + "DESCRIPTOR": _CHALLENGESOLUTION, + "__module__": "spotify.login5.v3.login5_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.ChallengeSolution) + }, +) +_sym_db.RegisterMessage(ChallengeSolution) + +LoginRequest = _reflection.GeneratedProtocolMessageType( + "LoginRequest", + (_message.Message, ), + { + "DESCRIPTOR": _LOGINREQUEST, + "__module__": "spotify.login5.v3.login5_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.LoginRequest) + }, +) +_sym_db.RegisterMessage(LoginRequest) + +LoginOk = _reflection.GeneratedProtocolMessageType( + "LoginOk", + (_message.Message, ), + { + "DESCRIPTOR": _LOGINOK, + "__module__": "spotify.login5.v3.login5_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.LoginOk) + }, +) +_sym_db.RegisterMessage(LoginOk) + +LoginResponse = _reflection.GeneratedProtocolMessageType( + "LoginResponse", + (_message.Message, ), + { + "DESCRIPTOR": _LOGINRESPONSE, + "__module__": "spotify.login5.v3.login5_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.LoginResponse) + }, +) +_sym_db.RegisterMessage(LoginResponse) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/spotify/login5/v3/UserInfo_pb2.py b/resources/lib/librespot/proto/spotify/login5/v3/UserInfo_pb2.py new file mode 100644 index 0000000..99f9235 --- /dev/null +++ b/resources/lib/librespot/proto/spotify/login5/v3/UserInfo_pb2.py @@ -0,0 +1,263 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: spotify/login5/v3/user_info.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="spotify/login5/v3/user_info.proto", + package="spotify.login5.v3", + syntax="proto3", + serialized_options=b"\n\024com.spotify.login5v3", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n!spotify/login5/v3/user_info.proto\x12\x11spotify.login5.v3"\x97\x02\n\x08UserInfo\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\r\n\x05\x65mail\x18\x02 \x01(\t\x12\x16\n\x0e\x65mail_verified\x18\x03 \x01(\x08\x12\x11\n\tbirthdate\x18\x04 \x01(\t\x12\x32\n\x06gender\x18\x05 \x01(\x0e\x32".spotify.login5.v3.UserInfo.Gender\x12\x14\n\x0cphone_number\x18\x06 \x01(\t\x12\x1d\n\x15phone_number_verified\x18\x07 \x01(\x08\x12 \n\x18\x65mail_already_registered\x18\x08 \x01(\x08"8\n\x06Gender\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x08\n\x04MALE\x10\x01\x12\n\n\x06\x46\x45MALE\x10\x02\x12\x0b\n\x07NEUTRAL\x10\x03\x42\x16\n\x14\x63om.spotify.login5v3b\x06proto3', +) + +_USERINFO_GENDER = _descriptor.EnumDescriptor( + name="Gender", + full_name="spotify.login5.v3.UserInfo.Gender", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="UNKNOWN", + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="MALE", + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="FEMALE", + index=2, + number=2, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="NEUTRAL", + index=3, + number=3, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=280, + serialized_end=336, +) +_sym_db.RegisterEnumDescriptor(_USERINFO_GENDER) + +_USERINFO = _descriptor.Descriptor( + name="UserInfo", + full_name="spotify.login5.v3.UserInfo", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="name", + full_name="spotify.login5.v3.UserInfo.name", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="email", + full_name="spotify.login5.v3.UserInfo.email", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="email_verified", + full_name="spotify.login5.v3.UserInfo.email_verified", + index=2, + number=3, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="birthdate", + full_name="spotify.login5.v3.UserInfo.birthdate", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="gender", + full_name="spotify.login5.v3.UserInfo.gender", + index=4, + number=5, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="phone_number", + full_name="spotify.login5.v3.UserInfo.phone_number", + index=5, + number=6, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="phone_number_verified", + full_name="spotify.login5.v3.UserInfo.phone_number_verified", + index=6, + number=7, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="email_already_registered", + full_name="spotify.login5.v3.UserInfo.email_already_registered", + index=7, + number=8, + type=8, + cpp_type=7, + label=1, + has_default_value=False, + default_value=False, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[ + _USERINFO_GENDER, + ], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=57, + serialized_end=336, +) + +_USERINFO.fields_by_name["gender"].enum_type = _USERINFO_GENDER +_USERINFO_GENDER.containing_type = _USERINFO +DESCRIPTOR.message_types_by_name["UserInfo"] = _USERINFO +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +UserInfo = _reflection.GeneratedProtocolMessageType( + "UserInfo", + (_message.Message, ), + { + "DESCRIPTOR": _USERINFO, + "__module__": "spotify.login5.v3.user_info_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.UserInfo) + }, +) +_sym_db.RegisterMessage(UserInfo) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/spotify/login5/v3/__init__.py b/resources/lib/librespot/proto/spotify/login5/v3/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/resources/lib/librespot/proto/spotify/login5/v3/challenges/Code_pb2.py b/resources/lib/librespot/proto/spotify/login5/v3/challenges/Code_pb2.py new file mode 100644 index 0000000..03dca14 --- /dev/null +++ b/resources/lib/librespot/proto/spotify/login5/v3/challenges/Code_pb2.py @@ -0,0 +1,224 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: spotify/login5/v3/challenges/code.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="spotify/login5/v3/challenges/code.proto", + package="spotify.login5.v3.challenges", + syntax="proto3", + serialized_options=b"\n\024com.spotify.login5v3", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n\'spotify/login5/v3/challenges/code.proto\x12\x1cspotify.login5.v3.challenges"\xbc\x01\n\rCodeChallenge\x12\x42\n\x06method\x18\x01 \x01(\x0e\x32\x32.spotify.login5.v3.challenges.CodeChallenge.Method\x12\x13\n\x0b\x63ode_length\x18\x02 \x01(\x05\x12\x12\n\nexpires_in\x18\x03 \x01(\x05\x12\x1e\n\x16\x63\x61nonical_phone_number\x18\x04 \x01(\t"\x1e\n\x06Method\x12\x0b\n\x07UNKNOWN\x10\x00\x12\x07\n\x03SMS\x10\x01"\x1c\n\x0c\x43odeSolution\x12\x0c\n\x04\x63ode\x18\x01 \x01(\tB\x16\n\x14\x63om.spotify.login5v3b\x06proto3', +) + +_CODECHALLENGE_METHOD = _descriptor.EnumDescriptor( + name="Method", + full_name="spotify.login5.v3.challenges.CodeChallenge.Method", + filename=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + values=[ + _descriptor.EnumValueDescriptor( + name="UNKNOWN", + index=0, + number=0, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + _descriptor.EnumValueDescriptor( + name="SMS", + index=1, + number=1, + serialized_options=None, + type=None, + create_key=_descriptor._internal_create_key, + ), + ], + containing_type=None, + serialized_options=None, + serialized_start=232, + serialized_end=262, +) +_sym_db.RegisterEnumDescriptor(_CODECHALLENGE_METHOD) + +_CODECHALLENGE = _descriptor.Descriptor( + name="CodeChallenge", + full_name="spotify.login5.v3.challenges.CodeChallenge", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="method", + full_name="spotify.login5.v3.challenges.CodeChallenge.method", + index=0, + number=1, + type=14, + cpp_type=8, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="code_length", + full_name="spotify.login5.v3.challenges.CodeChallenge.code_length", + index=1, + number=2, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="expires_in", + full_name="spotify.login5.v3.challenges.CodeChallenge.expires_in", + index=2, + number=3, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="canonical_phone_number", + full_name= + "spotify.login5.v3.challenges.CodeChallenge.canonical_phone_number", + index=3, + number=4, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[ + _CODECHALLENGE_METHOD, + ], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=74, + serialized_end=262, +) + +_CODESOLUTION = _descriptor.Descriptor( + name="CodeSolution", + full_name="spotify.login5.v3.challenges.CodeSolution", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="code", + full_name="spotify.login5.v3.challenges.CodeSolution.code", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=264, + serialized_end=292, +) + +_CODECHALLENGE.fields_by_name["method"].enum_type = _CODECHALLENGE_METHOD +_CODECHALLENGE_METHOD.containing_type = _CODECHALLENGE +DESCRIPTOR.message_types_by_name["CodeChallenge"] = _CODECHALLENGE +DESCRIPTOR.message_types_by_name["CodeSolution"] = _CODESOLUTION +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +CodeChallenge = _reflection.GeneratedProtocolMessageType( + "CodeChallenge", + (_message.Message, ), + { + "DESCRIPTOR": _CODECHALLENGE, + "__module__": "spotify.login5.v3.challenges.code_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.challenges.CodeChallenge) + }, +) +_sym_db.RegisterMessage(CodeChallenge) + +CodeSolution = _reflection.GeneratedProtocolMessageType( + "CodeSolution", + (_message.Message, ), + { + "DESCRIPTOR": _CODESOLUTION, + "__module__": "spotify.login5.v3.challenges.code_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.challenges.CodeSolution) + }, +) +_sym_db.RegisterMessage(CodeSolution) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/spotify/login5/v3/challenges/Hashcash_pb2.py b/resources/lib/librespot/proto/spotify/login5/v3/challenges/Hashcash_pb2.py new file mode 100644 index 0000000..d12b2a5 --- /dev/null +++ b/resources/lib/librespot/proto/spotify/login5/v3/challenges/Hashcash_pb2.py @@ -0,0 +1,176 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: spotify/login5/v3/challenges/hashcash.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import \ + duration_pb2 as google_dot_protobuf_dot_duration__pb2 +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="spotify/login5/v3/challenges/hashcash.proto", + package="spotify.login5.v3.challenges", + syntax="proto3", + serialized_options=b"\n\024com.spotify.login5v3", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n+spotify/login5/v3/challenges/hashcash.proto\x12\x1cspotify.login5.v3.challenges\x1a\x1egoogle/protobuf/duration.proto"3\n\x11HashcashChallenge\x12\x0e\n\x06prefix\x18\x01 \x01(\x0c\x12\x0e\n\x06length\x18\x02 \x01(\x05"O\n\x10HashcashSolution\x12\x0e\n\x06suffix\x18\x01 \x01(\x0c\x12+\n\x08\x64uration\x18\x02 \x01(\x0b\x32\x19.google.protobuf.DurationB\x16\n\x14\x63om.spotify.login5v3b\x06proto3', + dependencies=[ + google_dot_protobuf_dot_duration__pb2.DESCRIPTOR, + ], +) + +_HASHCASHCHALLENGE = _descriptor.Descriptor( + name="HashcashChallenge", + full_name="spotify.login5.v3.challenges.HashcashChallenge", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="prefix", + full_name="spotify.login5.v3.challenges.HashcashChallenge.prefix", + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="length", + full_name="spotify.login5.v3.challenges.HashcashChallenge.length", + index=1, + number=2, + type=5, + cpp_type=1, + label=1, + has_default_value=False, + default_value=0, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=109, + serialized_end=160, +) + +_HASHCASHSOLUTION = _descriptor.Descriptor( + name="HashcashSolution", + full_name="spotify.login5.v3.challenges.HashcashSolution", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="suffix", + full_name="spotify.login5.v3.challenges.HashcashSolution.suffix", + index=0, + number=1, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="duration", + full_name="spotify.login5.v3.challenges.HashcashSolution.duration", + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=162, + serialized_end=241, +) + +_HASHCASHSOLUTION.fields_by_name[ + "duration"].message_type = google_dot_protobuf_dot_duration__pb2._DURATION +DESCRIPTOR.message_types_by_name["HashcashChallenge"] = _HASHCASHCHALLENGE +DESCRIPTOR.message_types_by_name["HashcashSolution"] = _HASHCASHSOLUTION +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +HashcashChallenge = _reflection.GeneratedProtocolMessageType( + "HashcashChallenge", + (_message.Message, ), + { + "DESCRIPTOR": _HASHCASHCHALLENGE, + "__module__": "spotify.login5.v3.challenges.hashcash_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.challenges.HashcashChallenge) + }, +) +_sym_db.RegisterMessage(HashcashChallenge) + +HashcashSolution = _reflection.GeneratedProtocolMessageType( + "HashcashSolution", + (_message.Message, ), + { + "DESCRIPTOR": _HASHCASHSOLUTION, + "__module__": "spotify.login5.v3.challenges.hashcash_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.challenges.HashcashSolution) + }, +) +_sym_db.RegisterMessage(HashcashSolution) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/spotify/login5/v3/challenges/__init__.py b/resources/lib/librespot/proto/spotify/login5/v3/challenges/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/resources/lib/librespot/proto/spotify/login5/v3/credentials/Credentials_pb2.py b/resources/lib/librespot/proto/spotify/login5/v3/credentials/Credentials_pb2.py new file mode 100644 index 0000000..f141f66 --- /dev/null +++ b/resources/lib/librespot/proto/spotify/login5/v3/credentials/Credentials_pb2.py @@ -0,0 +1,483 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: spotify/login5/v3/credentials/credentials.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="spotify/login5/v3/credentials/credentials.proto", + package="spotify.login5.v3.credentials", + syntax="proto3", + serialized_options=b"\n\024com.spotify.login5v3", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n/spotify/login5/v3/credentials/credentials.proto\x12\x1dspotify.login5.v3.credentials"2\n\x10StoredCredential\x12\x10\n\x08username\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c"9\n\x08Password\x12\n\n\x02id\x18\x01 \x01(\t\x12\x10\n\x08password\x18\x02 \x01(\t\x12\x0f\n\x07padding\x18\x03 \x01(\x0c";\n\x13\x46\x61\x63\x65\x62ookAccessToken\x12\x0e\n\x06\x66\x62_uid\x18\x01 \x01(\t\x12\x14\n\x0c\x61\x63\x63\x65ss_token\x18\x02 \x01(\t"\x1d\n\x0cOneTimeToken\x12\r\n\x05token\x18\x01 \x01(\t"|\n\x15ParentChildCredential\x12\x10\n\x08\x63hild_id\x18\x01 \x01(\t\x12Q\n\x18parent_stored_credential\x18\x02 \x01(\x0b\x32/.spotify.login5.v3.credentials.StoredCredential"S\n\x15\x41ppleSignInCredential\x12\x11\n\tauth_code\x18\x01 \x01(\t\x12\x14\n\x0credirect_uri\x18\x02 \x01(\t\x12\x11\n\tbundle_id\x18\x03 \x01(\tB\x16\n\x14\x63om.spotify.login5v3b\x06proto3', +) + +_STOREDCREDENTIAL = _descriptor.Descriptor( + name="StoredCredential", + full_name="spotify.login5.v3.credentials.StoredCredential", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="username", + full_name="spotify.login5.v3.credentials.StoredCredential.username", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="data", + full_name="spotify.login5.v3.credentials.StoredCredential.data", + index=1, + number=2, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=82, + serialized_end=132, +) + +_PASSWORD = _descriptor.Descriptor( + name="Password", + full_name="spotify.login5.v3.credentials.Password", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="id", + full_name="spotify.login5.v3.credentials.Password.id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="password", + full_name="spotify.login5.v3.credentials.Password.password", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="padding", + full_name="spotify.login5.v3.credentials.Password.padding", + index=2, + number=3, + type=12, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"", + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=134, + serialized_end=191, +) + +_FACEBOOKACCESSTOKEN = _descriptor.Descriptor( + name="FacebookAccessToken", + full_name="spotify.login5.v3.credentials.FacebookAccessToken", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="fb_uid", + full_name= + "spotify.login5.v3.credentials.FacebookAccessToken.fb_uid", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="access_token", + full_name= + "spotify.login5.v3.credentials.FacebookAccessToken.access_token", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=193, + serialized_end=252, +) + +_ONETIMETOKEN = _descriptor.Descriptor( + name="OneTimeToken", + full_name="spotify.login5.v3.credentials.OneTimeToken", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="token", + full_name="spotify.login5.v3.credentials.OneTimeToken.token", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=254, + serialized_end=283, +) + +_PARENTCHILDCREDENTIAL = _descriptor.Descriptor( + name="ParentChildCredential", + full_name="spotify.login5.v3.credentials.ParentChildCredential", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="child_id", + full_name= + "spotify.login5.v3.credentials.ParentChildCredential.child_id", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="parent_stored_credential", + full_name= + "spotify.login5.v3.credentials.ParentChildCredential.parent_stored_credential", + index=1, + number=2, + type=11, + cpp_type=10, + label=1, + has_default_value=False, + default_value=None, + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=285, + serialized_end=409, +) + +_APPLESIGNINCREDENTIAL = _descriptor.Descriptor( + name="AppleSignInCredential", + full_name="spotify.login5.v3.credentials.AppleSignInCredential", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="auth_code", + full_name= + "spotify.login5.v3.credentials.AppleSignInCredential.auth_code", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="redirect_uri", + full_name= + "spotify.login5.v3.credentials.AppleSignInCredential.redirect_uri", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="bundle_id", + full_name= + "spotify.login5.v3.credentials.AppleSignInCredential.bundle_id", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=411, + serialized_end=494, +) + +_PARENTCHILDCREDENTIAL.fields_by_name[ + "parent_stored_credential"].message_type = _STOREDCREDENTIAL +DESCRIPTOR.message_types_by_name["StoredCredential"] = _STOREDCREDENTIAL +DESCRIPTOR.message_types_by_name["Password"] = _PASSWORD +DESCRIPTOR.message_types_by_name["FacebookAccessToken"] = _FACEBOOKACCESSTOKEN +DESCRIPTOR.message_types_by_name["OneTimeToken"] = _ONETIMETOKEN +DESCRIPTOR.message_types_by_name[ + "ParentChildCredential"] = _PARENTCHILDCREDENTIAL +DESCRIPTOR.message_types_by_name[ + "AppleSignInCredential"] = _APPLESIGNINCREDENTIAL +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +StoredCredential = _reflection.GeneratedProtocolMessageType( + "StoredCredential", + (_message.Message, ), + { + "DESCRIPTOR": _STOREDCREDENTIAL, + "__module__": "spotify.login5.v3.credentials.credentials_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.credentials.StoredCredential) + }, +) +_sym_db.RegisterMessage(StoredCredential) + +Password = _reflection.GeneratedProtocolMessageType( + "Password", + (_message.Message, ), + { + "DESCRIPTOR": _PASSWORD, + "__module__": "spotify.login5.v3.credentials.credentials_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.credentials.Password) + }, +) +_sym_db.RegisterMessage(Password) + +FacebookAccessToken = _reflection.GeneratedProtocolMessageType( + "FacebookAccessToken", + (_message.Message, ), + { + "DESCRIPTOR": _FACEBOOKACCESSTOKEN, + "__module__": "spotify.login5.v3.credentials.credentials_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.credentials.FacebookAccessToken) + }, +) +_sym_db.RegisterMessage(FacebookAccessToken) + +OneTimeToken = _reflection.GeneratedProtocolMessageType( + "OneTimeToken", + (_message.Message, ), + { + "DESCRIPTOR": _ONETIMETOKEN, + "__module__": "spotify.login5.v3.credentials.credentials_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.credentials.OneTimeToken) + }, +) +_sym_db.RegisterMessage(OneTimeToken) + +ParentChildCredential = _reflection.GeneratedProtocolMessageType( + "ParentChildCredential", + (_message.Message, ), + { + "DESCRIPTOR": _PARENTCHILDCREDENTIAL, + "__module__": "spotify.login5.v3.credentials.credentials_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.credentials.ParentChildCredential) + }, +) +_sym_db.RegisterMessage(ParentChildCredential) + +AppleSignInCredential = _reflection.GeneratedProtocolMessageType( + "AppleSignInCredential", + (_message.Message, ), + { + "DESCRIPTOR": _APPLESIGNINCREDENTIAL, + "__module__": "spotify.login5.v3.credentials.credentials_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.credentials.AppleSignInCredential) + }, +) +_sym_db.RegisterMessage(AppleSignInCredential) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/spotify/login5/v3/credentials/__init__.py b/resources/lib/librespot/proto/spotify/login5/v3/credentials/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/resources/lib/librespot/proto/spotify/login5/v3/identifiers/Identifiers.py b/resources/lib/librespot/proto/spotify/login5/v3/identifiers/Identifiers.py new file mode 100644 index 0000000..42457c5 --- /dev/null +++ b/resources/lib/librespot/proto/spotify/login5/v3/identifiers/Identifiers.py @@ -0,0 +1,119 @@ +# -*- coding: utf-8 -*- +# Generated by the protocol buffer compiler. DO NOT EDIT! +# source: spotify/login5/v3/identifiers/identifiers.proto +"""Generated protocol buffer code.""" +from google.protobuf import descriptor as _descriptor +from google.protobuf import message as _message +from google.protobuf import reflection as _reflection +from google.protobuf import symbol_database as _symbol_database + +# @@protoc_insertion_point(imports) + +_sym_db = _symbol_database.Default() + +DESCRIPTOR = _descriptor.FileDescriptor( + name="spotify/login5/v3/identifiers/identifiers.proto", + package="spotify.login5.v3.identifiers", + syntax="proto3", + serialized_options=b"\n\024com.spotify.login5v3", + create_key=_descriptor._internal_create_key, + serialized_pb= + b'\n/spotify/login5/v3/identifiers/identifiers.proto\x12\x1dspotify.login5.v3.identifiers"U\n\x0bPhoneNumber\x12\x0e\n\x06number\x18\x01 \x01(\t\x12\x18\n\x10iso_country_code\x18\x02 \x01(\t\x12\x1c\n\x14\x63ountry_calling_code\x18\x03 \x01(\tB\x16\n\x14\x63om.spotify.login5v3b\x06proto3', +) + +_PHONENUMBER = _descriptor.Descriptor( + name="PhoneNumber", + full_name="spotify.login5.v3.identifiers.PhoneNumber", + filename=None, + file=DESCRIPTOR, + containing_type=None, + create_key=_descriptor._internal_create_key, + fields=[ + _descriptor.FieldDescriptor( + name="number", + full_name="spotify.login5.v3.identifiers.PhoneNumber.number", + index=0, + number=1, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="iso_country_code", + full_name= + "spotify.login5.v3.identifiers.PhoneNumber.iso_country_code", + index=1, + number=2, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + _descriptor.FieldDescriptor( + name="country_calling_code", + full_name= + "spotify.login5.v3.identifiers.PhoneNumber.country_calling_code", + index=2, + number=3, + type=9, + cpp_type=9, + label=1, + has_default_value=False, + default_value=b"".decode("utf-8"), + message_type=None, + enum_type=None, + containing_type=None, + is_extension=False, + extension_scope=None, + serialized_options=None, + file=DESCRIPTOR, + create_key=_descriptor._internal_create_key, + ), + ], + extensions=[], + nested_types=[], + enum_types=[], + serialized_options=None, + is_extendable=False, + syntax="proto3", + extension_ranges=[], + oneofs=[], + serialized_start=82, + serialized_end=167, +) + +DESCRIPTOR.message_types_by_name["PhoneNumber"] = _PHONENUMBER +_sym_db.RegisterFileDescriptor(DESCRIPTOR) + +PhoneNumber = _reflection.GeneratedProtocolMessageType( + "PhoneNumber", + (_message.Message, ), + { + "DESCRIPTOR": _PHONENUMBER, + "__module__": "spotify.login5.v3.identifiers.identifiers_pb2" + # @@protoc_insertion_point(class_scope:spotify.login5.v3.identifiers.PhoneNumber) + }, +) +_sym_db.RegisterMessage(PhoneNumber) + +DESCRIPTOR._options = None +# @@protoc_insertion_point(module_scope) diff --git a/resources/lib/librespot/proto/spotify/login5/v3/identifiers/__init__.py b/resources/lib/librespot/proto/spotify/login5/v3/identifiers/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/resources/lib/librespot/structure.py b/resources/lib/librespot/structure.py new file mode 100644 index 0000000..9171ada --- /dev/null +++ b/resources/lib/librespot/structure.py @@ -0,0 +1,103 @@ +from __future__ import annotations +import typing + +if typing.TYPE_CHECKING: + from librespot.audio import AbsChunkedInputStream + from librespot.audio.format import SuperAudioFormat + from librespot.core import DealerClient, Session + from librespot.crypto import Packet + from librespot.mercury import MercuryClient + from librespot.proto import Metadata_pb2 as Metadata + + +class AudioDecrypt: + def decrypt_chunk(self, chunk_index: int, buffer: bytes): + raise NotImplementedError + + def decrypt_time_ms(self): + raise NotImplementedError + + +class AudioQualityPicker: + def get_file(self, + files: typing.List[Metadata.AudioFile]) -> Metadata.AudioFile: + raise NotImplementedError + + +class Closeable: + def close(self) -> None: + raise NotImplementedError + + +class FeederException(Exception): + pass + + +class GeneralAudioStream: + def stream(self) -> AbsChunkedInputStream: + raise NotImplementedError + + def codec(self) -> SuperAudioFormat: + raise NotImplementedError + + def describe(self) -> str: + raise NotImplementedError + + def decrypt_time_ms(self) -> int: + raise NotImplementedError + + +class GeneralWritableStream: + def write_chunk(self, buffer: bytearray, chunk_index: int, cached: bool): + raise NotImplementedError + + +class HaltListener: + def stream_read_halted(self, chunk: int, _time: int) -> None: + raise NotImplementedError + + def stream_read_resumed(self, chunk: int, _time: int) -> None: + raise NotImplementedError + + +class MessageListener: + def on_message(self, uri: str, headers: typing.Dict[str, str], + payload: bytes): + raise NotImplementedError + + +class NoopAudioDecrypt(AudioDecrypt): + def decrypt_chunk(self, chunk_index: int, buffer: bytes): + return buffer + + def decrypt_time_ms(self): + return 0 + + +class PacketsReceiver: + def dispatch(self, packet: Packet): + raise NotImplementedError + + +class RequestListener: + def on_request(self, mid: str, pid: int, sender: str, + command: typing.Any) -> DealerClient.RequestResult: + raise NotImplementedError + + +class Runnable: + def run(self): + raise NotImplementedError + + +class SessionListener: + def session_closing(self, session: Session) -> None: + raise NotImplementedError + + def session_changed(self, session: Session) -> None: + raise NotImplementedError + + +class SubListener: + def event(self, resp: MercuryClient.Response) -> None: + raise NotImplementedError diff --git a/resources/lib/librespot/util.py b/resources/lib/librespot/util.py new file mode 100644 index 0000000..28d7441 --- /dev/null +++ b/resources/lib/librespot/util.py @@ -0,0 +1,117 @@ +from Cryptodome import Random +import binascii +import math + + +def bytes_to_hex(buffer: bytes) -> str: + """ + Convert bytes to hex + Args: + buffer: Bytes to convert + Returns: + hex + """ + return binascii.hexlify(buffer).decode() + + +def hex_to_bytes(s: str) -> bytes: + return binascii.unhexlify(s) + + +def int_to_bytes(i: int): + """ + Convert an integer to a byte(s) + Args: + i: Integer to convert + Returns: + bytes + """ + width = i.bit_length() + width += 8 - ((width % 8) or 8) + fmt = '%%0%dx' % (width // 4) + return b"\x00" if i == 0 else binascii.unhexlify(fmt % i) + + +def random_hex_string(length: int): + buffer = Random.get_random_bytes(int(length / 2)) + return bytes_to_hex(buffer) + + +class Base62: + standard_base = 256 + target_base = 62 + alphabet: bytes + lookup: bytearray + + def __init__(self, alphabet: bytes): + self.alphabet = alphabet + self.create_lookup_table() + + @staticmethod + def create_instance_with_inverted_character_set(): + return Base62(Base62.CharacterSets.inverted) + + def encode(self, message: bytes, length: int = -1): + indices = self.convert(message, self.standard_base, self.target_base, + length) + return self.translate(indices, self.alphabet) + + def decode(self, encoded: bytes, length: int = -1): + prepared = self.translate(encoded, self.lookup) + return self.convert(prepared, self.target_base, self.standard_base, + length) + + def translate(self, indices: bytes, dictionary: bytes): + translation = bytearray(len(indices)) + for i in range(len(indices)): + translation[i] = dictionary[int.from_bytes(bytes([indices[i]]), "big")] + return translation + + def convert(self, message: bytes, source_base: int, target_base: int, + length: int): + estimated_length = self.estimate_output_length( + len(message), source_base, target_base) if length == -1 else length + out = b"" + source = message + while len(source) > 0: + quotient = b"" + remainder = 0 + for b in source: + accumulator = int(b & 0xff) + remainder * source_base + digit = int( + (accumulator - (accumulator % target_base)) / target_base) + remainder = int(accumulator % target_base) + if len(quotient) > 0 or digit > 0: + quotient += bytes([digit]) + out += bytes([remainder]) + source = quotient + if len(out) < estimated_length: + size = len(out) + for _ in range(estimated_length - size): + out += bytes([0]) + return self.reverse(out) + if len(out) > estimated_length: + return self.reverse(out[:estimated_length]) + return self.reverse(out) + + def estimate_output_length(self, input_length: int, source_base: int, + target_base: int): + return int( + math.ceil((math.log(source_base) / math.log(target_base)) * + input_length)) + + def reverse(self, arr: bytes): + length = len(arr) + reversed_arr = bytearray(length) + for i in range(length): + reversed_arr[length - i - 1] = arr[i] + return bytes(reversed_arr) + + def create_lookup_table(self): + self.lookup = bytearray(256) + for i in range(len(self.alphabet)): + self.lookup[self.alphabet[i]] = i & 0xff + + class CharacterSets: + gmp = b'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + inverted = b'0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ' diff --git a/resources/lib/librespot/zeroconf.py b/resources/lib/librespot/zeroconf.py new file mode 100644 index 0000000..afe6847 --- /dev/null +++ b/resources/lib/librespot/zeroconf.py @@ -0,0 +1,345 @@ +from __future__ import annotations +from Cryptodome.Cipher import AES +from Cryptodome.Hash import HMAC, SHA1 +from Cryptodome.Util import Counter +from librespot import util, Version +from librespot.core import Session +from librespot.crypto import DiffieHellman +from librespot.proto import Connect_pb2 as Connect +from librespot.structure import Closeable, Runnable, SessionListener +import base64 +import concurrent.futures +import copy +import io +import json +import logging +import random +import socket +import threading +import typing +import urllib.parse +import zeroconf + + +class ZeroconfServer(Closeable): + logger = logging.getLogger("Librespot:ZeroconfServer") + service = "_spotify-connect._tcp.local." + __connecting_username: typing.Union[str, None] = None + __connection_lock = threading.Condition() + __default_get_info_fields = { + "status": 101, + "statusString": "OK", + "spotifyError": 0, + "version": "2.7.1", + "libraryVersion": Version.version_name, + "accountReq": "PREMIUM", + "brandDisplayName": "kokarare1212", + "modelDisplayName": "librespot-python", + "voiceSupport": "NO", + "availability": "", + "productID": 0, + "tokenType": "default", + "groupStatus": "NONE", + "resolverVersion": "0", + "scope": "streaming,client-authorization-universal", + } + __default_successful_add_user = { + "status": 101, + "spotifyError": 0, + "statusString": "OK", + } + __eol = b"\r\n" + __max_port = 65536 + __min_port = 1024 + __runner: HttpRunner + __service_info: zeroconf.ServiceInfo + __session: typing.Union[Session, None] = None + __session_listeners: typing.List[SessionListener] = [] + __zeroconf: zeroconf.Zeroconf + + def __init__(self, inner: Inner, listen_port): + self.__inner = inner + self.__keys = DiffieHellman() + if listen_port == -1: + listen_port = random.randint(self.__min_port + 1, self.__max_port) + self.__runner = ZeroconfServer.HttpRunner(self, listen_port) + threading.Thread(target=self.__runner.run, + name="zeroconf-http-server").start() + self.__zeroconf = zeroconf.Zeroconf() + self.__service_info = zeroconf.ServiceInfo( + ZeroconfServer.service, + inner.device_name + "." + ZeroconfServer.service, + listen_port, + 0, + 0, { + "CPath": "/", + "VERSION": "1.0", + "STACK": "SP", + }, + self.get_useful_hostname() + ".", + addresses=[ + socket.inet_aton( + socket.gethostbyname(self.get_useful_hostname())) + ]) + self.__zeroconf.register_service(self.__service_info) + threading.Thread(target=self.__zeroconf.start, + name="zeroconf-multicast-dns-server").start() + + def add_session_listener(self, listener: ZeroconfServer): + self.__session_listeners.append(listener) + + def close(self) -> None: + self.__zeroconf.close() + self.__runner.close() + + def close_session(self) -> None: + if self.__session is None: + return + for session_listener in self.__session_listeners: + session_listener.session_closing(self.__session) + self.__session.close() + self.__session = None + + def get_useful_hostname(self) -> str: + host = socket.gethostname() + if host == "localhost": + pass + else: + return host + + def handle_add_user(self, __socket: socket.socket, params: dict[str, str], + http_version: str) -> None: + username = params.get("userName") + if not username: + self.logger.error("Missing userName!") + return + blob_str = params.get("blob") + if not blob_str: + self.logger.error("Missing blob!") + return + client_key_str = params.get("clientKey") + if not client_key_str: + self.logger.error("Missing clientKey!") + with self.__connection_lock: + if username == self.__connecting_username: + self.logger.info( + "{} is already trying to connect.".format(username)) + __socket.send(http_version.encode()) + __socket.send(b" 403 Forbidden") + __socket.send(self.__eol) + __socket.send(self.__eol) + return + shared_key = util.int_to_bytes( + self.__keys.compute_shared_key( + base64.b64decode(client_key_str.encode()))) + blob_bytes = base64.b64decode(blob_str) + iv = blob_bytes[:16] + encrypted = blob_bytes[16:len(blob_bytes) - 20] + checksum = blob_bytes[len(blob_bytes) - 20:] + sha1 = SHA1.new() + sha1.update(shared_key) + base_key = sha1.digest()[:16] + hmac = HMAC.new(base_key, digestmod=SHA1) + hmac.update(b"checksum") + checksum_key = hmac.digest() + hmac = HMAC.new(base_key, digestmod=SHA1) + hmac.update(b"encryption") + encryption_key = hmac.digest() + hmac = HMAC.new(checksum_key, digestmod=SHA1) + hmac.update(encrypted) + mac = hmac.digest() + if mac != checksum: + self.logger.error("Mac and checksum don't match!") + __socket.send(http_version.encode()) + __socket.send(b" 400 Bad Request") + __socket.send(self.__eol) + __socket.send(self.__eol) + return + aes = AES.new(encryption_key[:16], + AES.MODE_CTR, + counter=Counter.new(128, + initial_value=int.from_bytes( + iv, "big"))) + decrypted = aes.decrypt(encrypted) + self.close_session() + with self.__connection_lock: + self.__connecting_username = username + self.logger.info("Accepted new user from {}. [deviceId: {}]".format( + params.get("deviceName"), self.__inner.device_id)) + response = json.dumps(self.__default_successful_add_user) + __socket.send(http_version.encode()) + __socket.send(b" 200 OK") + __socket.send(self.__eol) + __socket.send(b"Content-Length: ") + __socket.send(str(len(response)).encode()) + __socket.send(self.__eol) + __socket.send(self.__eol) + __socket.send(response.encode()) + self.__session = Session.Builder(self.__inner.conf) \ + .set_device_id(self.__inner.device_id) \ + .set_device_name(self.__inner.device_name) \ + .set_device_type(self.__inner.device_type) \ + .set_preferred_locale(self.__inner.preferred_locale) \ + .blob(username, decrypted) \ + .create() + with self.__connection_lock: + self.__connecting_username = None + for session_listener in self.__session_listeners: + session_listener.session_changed(self.__session) + + def handle_get_info(self, __socket: socket.socket, + http_version: str) -> None: + info = copy.deepcopy(self.__default_get_info_fields) + info["deviceID"] = self.__inner.device_id + info["remoteName"] = self.__inner.device_name + info["publicKey"] = base64.b64encode( + self.__keys.public_key_bytes()).decode() + info["deviceType"] = Connect.DeviceType.Name(self.__inner.device_type) + with self.__connection_lock: + info[ + "activeUser"] = self.__connecting_username if self.__connecting_username is not None else self.__session.username( + ) if self.has_valid_session() else "" + __socket.send(http_version.encode()) + __socket.send(b" 200 OK") + __socket.send(self.__eol) + __socket.send(b"Content-Type: application/json") + __socket.send(self.__eol) + __socket.send(self.__eol) + __socket.send(json.dumps(info).encode()) + + def has_valid_session(self) -> bool: + valid = self.__session and self.__session.is_valid() + if not valid: + self.__session = None + return valid + + def parse_path(self, path: str) -> dict[str, str]: + url = "https://host" + path + parsed = {} + params = urllib.parse.parse_qs(urllib.parse.urlparse(url).query) + for key, values in params.items(): + for value in values: + parsed[key] = value + return parsed + + def remove_session_listener(self, listener: SessionListener): + self.__session_listeners.remove(listener) + + class Builder(Session.Builder): + listen_port: int = -1 + + def set_listen_port(self, listen_port: int): + self.listen_port = listen_port + return self + + def create(self) -> ZeroconfServer: + return ZeroconfServer( + ZeroconfServer.Inner(self.device_type, self.device_name, + self.device_id, self.preferred_locale, + self.conf), self.listen_port) + + class HttpRunner(Closeable, Runnable): + __should_stop = False + __socket: socket.socket + __worker = concurrent.futures.ThreadPoolExecutor() + __zeroconf_server: ZeroconfServer + + def __init__(self, zeroconf_server: ZeroconfServer, port: int): + self.__socket = socket.socket() + self.__socket.bind((".".join(["0"] * 4), port)) + self.__socket.listen(5) + self.__zeroconf_server = zeroconf_server + self.__zeroconf_server.logger.info( + "Zeroconf HTTP server started successfully on port {}!".format( + port)) + + def close(self) -> None: + pass + + def run(self): + while not self.__should_stop: + __socket, address = self.__socket.accept() + + def anonymous(): + self.__handle(__socket) + __socket.close() + + self.__worker.submit(anonymous) + + def __handle(self, __socket: socket.socket) -> None: + request = io.BytesIO(__socket.recv(1024 * 1024)) + request_line = request.readline().strip().split(b" ") + if len(request_line) != 3: + self.__zeroconf_server.logger.warning( + "Unexpected request line: {}".format(request_line)) + method = request_line[0].decode() + path = request_line[1].decode() + http_version = request_line[2].decode() + headers = {} + while True: + header = request.readline().strip() + if not header: + break + split = header.split(b":") + headers[split[0].decode()] = split[1].strip().decode() + if not self.__zeroconf_server.has_valid_session(): + self.__zeroconf_server.logger.debug( + "Handling request: {}, {}, {}, headers: {}".format( + method, path, http_version, headers)) + params = {} + if method == "POST": + content_type = headers.get("Content-Type") + if content_type != "application/x-www-form-urlencoded": + self.__zeroconf_server.logger.error( + "Bad Content-Type: {}".format(content_type)) + return + content_length_str = headers.get("Content-Length") + if content_length_str is None: + self.__zeroconf_server.logger.error( + "Missing Content-Length header!") + return + content_length = int(content_length_str) + body = request.read(content_length).decode() + pairs = body.split("&") + for pair in pairs: + split = pair.split("=") + params[urllib.parse.unquote( + split[0])] = urllib.parse.unquote(split[1]) + else: + params = self.__zeroconf_server.parse_path(path) + action = params.get("action") + if action is None: + self.__zeroconf_server.logger.debug( + "Request is missing action.") + return + self.handle_request(__socket, http_version, action, params) + + def handle_request(self, __socket: socket.socket, http_version: str, + action: str, params: dict[str, str]) -> None: + if action == "addUser": + if params is None: + raise RuntimeError + self.__zeroconf_server.handle_add_user(__socket, params, + http_version) + elif action == "getInfo": + self.__zeroconf_server.handle_get_info(__socket, http_version) + else: + self.__zeroconf_server.logger.warning( + "Unknown action: {}".format(action)) + + class Inner: + conf: typing.Final[Session.Configuration] + device_name: typing.Final[str] + device_id: typing.Final[str] + device_type: typing.Final[Connect.DeviceType] + preferred_locale: typing.Final[str] + + def __init__(self, device_type: Connect.DeviceType, device_name: str, + device_id: str, preferred_locale: str, + conf: Session.Configuration): + self.conf = conf + self.device_name = device_name + self.device_id = util.random_hex_string( + 40).lower() if not device_id else device_id + self.device_type = device_type + self.preferred_locale = preferred_locale diff --git a/resources/lib/librespot_auth.py b/resources/lib/librespot_auth.py new file mode 100644 index 0000000..1dba7b1 --- /dev/null +++ b/resources/lib/librespot_auth.py @@ -0,0 +1,66 @@ +import time +from typing import Dict + +from librespot.core import Session +from utils import log_msg, log_exception, LOGDEBUG + +CLIENT_ID = "2eb96f9b37494be1824999d58028a305" +SPOTTY_SCOPE = [ + "user-read-playback-state", + "user-read-currently-playing", + "user-modify-playback-state", + "playlist-read-private", + "playlist-read-collaborative", + "playlist-modify-public", + "playlist-modify-private", + "user-follow-modify", + "user-follow-read", + "user-library-read", + "user-library-modify", + "user-read-private", + "user-read-email", + "user-read-birthdate", + "user-top-read", +] + + +class LibrespotAuth: + def __init__(self, session: Session): + self.__session = session + + def get_token(self) -> Dict[str, str]: + token_info = None + + try: + tokenprovider = self.__session.tokens() + result = tokenprovider.get_token( + "user-read-playback-state", + "user-read-currently-playing", + "user-modify-playback-state", + "playlist-read-private", + "playlist-read-collaborative", + "playlist-modify-public", + "playlist-modify-private", + "user-follow-modify", + "user-follow-read", + "user-library-read", + "user-library-modify", + "user-read-private", + "user-read-email", + "user-read-birthdate", + "user-top-read", + ) + + # Transform token info to spotipy compatible format. + if result is not None: + token_info = { + "access_token": result.access_token, + "expires_in": result.expires_in, + "expires_at": int(time.time()) + result.expires_in, + "refresh_token": result.access_token, + } + log_msg(f"Token: {token_info}", LOGDEBUG) + except Exception as exc: + log_exception(exc, "Get Spotify token error") + + return token_info diff --git a/resources/lib/main_service.py b/resources/lib/main_service.py new file mode 100644 index 0000000..fadc79b --- /dev/null +++ b/resources/lib/main_service.py @@ -0,0 +1,151 @@ +""" + plugin.audio.librespot + Spotify player for Kodi + main_service.py + Background service which launches the http service. +""" + +import time +from typing import Dict + +import xbmc +import xbmcaddon +import xbmcgui +from xbmc import LOGDEBUG, LOGWARNING + +from bottle_manager import LibrespotServer +import utils +from http_video_player_setter import HttpVideoPlayerSetter +from save_recently_played import SaveRecentlyPlayed +from string_ids import HTTP_VIDEO_RULE_ADDED_STR_ID +from utils import PROXY_PORT, log_msg, ADDON_ID + +from librespot_auth import LibrespotAuth + +from librespot.core import Session + +SAVE_TO_RECENTLY_PLAYED_FILE = True + +SPOTIFY_ADDON = xbmcaddon.Addon(id=ADDON_ID) + + +def abort_app(timeout_in_secs: int) -> bool: + return xbmc.Monitor().waitForAbort(timeout_in_secs) + + +def add_http_video_rule() -> None: + video_player_setter = HttpVideoPlayerSetter() + + if not video_player_setter.set_http_rule(): + return + + msg = SPOTIFY_ADDON.getLocalizedString(HTTP_VIDEO_RULE_ADDED_STR_ID) + dialog = xbmcgui.Dialog() + header = SPOTIFY_ADDON.getAddonInfo("name") + dialog.ok(header, msg) + +def get_username() -> str: + addon = xbmcaddon.Addon(id=ADDON_ID) + spotify_username = addon.getSetting("username") + if not spotify_username: + raise Exception("Could not get spotify username.") + return spotify_username + +def get_password() -> str: + addon = xbmcaddon.Addon(id=ADDON_ID) + spotify_password = addon.getSetting("password") + if not spotify_password: + raise Exception("Could not get spotify password.") + return spotify_password + +class MainService: + def __init__(self): + log_msg(f"Spotify plugin version: {xbmcaddon.Addon(id=ADDON_ID).getAddonInfo('version')}.") + + self.__librespot_session: Session = Session.Builder().user_pass(get_username(), get_password()).create() + + add_http_video_rule() + + self.__librespot_auth: LibrespotAuth = LibrespotAuth(self.__librespot_session) + self.__auth_token: Dict[str, str] = dict() + + self.__save_recently_played: SaveRecentlyPlayed = SaveRecentlyPlayed() + + def __save_track_to_recently_played(self, track_id: str) -> None: + if SAVE_TO_RECENTLY_PLAYED_FILE: + self.__save_recently_played.save_track(track_id) + + def run(self) -> None: + log_msg("Starting main service loop.") + self.__renew_token() + + librespot_server = LibrespotServer(self.__librespot_session) + librespot_server.run(host='127.0.0.1', port=PROXY_PORT) + log_msg(f"Started bottle with port {PROXY_PORT}.") + + + loop_counter = 0 + loop_wait_in_secs = 6 + while True: + loop_counter += 1 + if (loop_counter % 10) == 0: + log_msg(f"Main loop continuing. Loop counter: {loop_counter}.") + + # Monitor authorization. + if (int(self.__auth_token["expires_at"]) - 60) <= (int(time.time())): + expire_time = int(self.__auth_token["expires_at"]) + time_now = int(time.time()) + log_msg( + f"Spotify token expired." + f" Expire time: {self.__get_time_str(expire_time)} ({expire_time});" + f" time now: {self.__get_time_str(time_now)} ({time_now})." + ) + log_msg("Refreshing auth token now.") + self.__renew_token() + + if abort_app(loop_wait_in_secs): + break + + librespot_server.close() + + def __renew_token(self) -> None: + log_msg("Retrieving auth token....", LOGDEBUG) + + self.__auth_token = self.__get_retry_auth_token() + if not self.__auth_token: + utils.cache_auth_token("") + raise Exception( + f"Could not get Spotify auth token for" + f" user '{self.__spotty_helper.get_username()}'." + ) + + log_msg( + f"Retrieved Spotify auth token." + f" Expires at {self.__get_time_str(int(self.__auth_token['expires_at']))}." + ) + + # Cache auth token for easy access by the plugin. + utils.cache_auth_token(self.__auth_token["access_token"]) + + def __get_retry_auth_token(self) -> Dict[str, str]: + auth_token = None + max_retries = 20 + count = 0 + while count < max_retries: + auth_token = self.__get_token() + if auth_token: + break + time.sleep(1) + count += 1 + + if count > 0: + log_msg(f"Took {count} retries to get authorization token.", LOGWARNING) + + return auth_token + + def __get_token(self) -> Dict[str, str]: + return self.__librespot_auth.get_token() + + @staticmethod + def __get_time_str(raw_time: int) -> str: + return time.strftime("%Y-%m-%d %H:%M:%S", time.localtime(float(raw_time))) diff --git a/resources/lib/plugin_content.py b/resources/lib/plugin_content.py new file mode 100644 index 0000000..3fcc03c --- /dev/null +++ b/resources/lib/plugin_content.py @@ -0,0 +1,1637 @@ +import math +import os +import sys +import time +import urllib.parse +from typing import Any, Dict, List, Tuple, Union + +import simplecache +import xbmc +import xbmcaddon +import xbmcgui +import xbmcplugin +import xbmcvfs + +import spotipy +import utils +from string_ids import * +from utils import ADDON_ID, PROXY_PORT, log_exception, log_msg, get_chunks + +MUSIC_ARTISTS_ICON = "icon_music_artists.png" +MUSIC_TOP_ARTISTS_ICON = "icon_music_top_artists.png" +MUSIC_SONGS_ICON = "icon_music_songs.png" +MUSIC_TOP_TRACKS_ICON = "icon_music_top_tracks.png" +MUSIC_ALBUMS_ICON = "icon_music_albums.png" +MUSIC_PLAYLISTS_ICON = "icon_music_playlists.png" +MUSIC_LIBRARY_ICON = "icon_music_library.png" +MUSIC_SEARCH_ICON = "icon_music_search.png" +MUSIC_EXPLORE_ICON = "icon_music_explore.png" +CLEAR_CACHE_ICON = "icon_clear_cache.png" + +Playlist = Dict[str, Union[str, Dict[str, List[Any]]]] + + +class PluginContent: + __addon: xbmcaddon.Addon = xbmcaddon.Addon(id=ADDON_ID) + __win: xbmcgui.Window = xbmcgui.Window(utils.ADDON_WINDOW_ID) + __addon_icon_path = os.path.join(__addon.getAddonInfo("path"), "resources") + __action = "" + __spotipy = None + __userid = "" + __user_country = "" + __offset = 0 + __playlist_id = "" + __album_id = "" + __track_id = "" + __artist_id = "" + __artist_name = "" + __owner_id = "" + __filter = "" + __token = "" + __limit = 50 + __params = {} + __base_url = sys.argv[0] + __addon_handle = int(sys.argv[1]) + __cached_checksum = "" + __last_playlist_position = 0 + + def __init__(self): + try: + self.cache: simplecache.SimpleCache = simplecache.SimpleCache(ADDON_ID) + + self.append_artist_to_title: bool = ( + self.__addon.getSetting("appendArtistToTitle") == "true" + ) + self.default_view_songs: str = self.__addon.getSetting("songDefaultView") + self.default_view_artists: str = self.__addon.getSetting("artistDefaultView") + self.default_view_playlists: str = self.__addon.getSetting("playlistDefaultView") + self.default_view_albums: str = self.__addon.getSetting("albumDefaultView") + self.default_view_category: str = self.__addon.getSetting("categoryDefaultView") + + auth_token: str = self.__get_authkey() + if not auth_token: + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + return + + self.__spotipy: spotipy.Spotify = spotipy.Spotify(auth=auth_token) + self.__userid: str = self.__spotipy.me()["id"] + self.__user_country = self.__spotipy.me()["country"] + + self.parse_params() + + if self.__action: + log_msg(f"Evaluating action '{self.__action}'.") + action = f"self.{self.__action}" + eval(action)() + else: + log_msg("Browsing main and setting up precache library.") + self.__browse_main() + self.__precache_library() + + except Exception as exc: + log_exception(exc, "PluginContent init error") + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + + def parse_params(self): + """parse parameters from the plugin entry path""" + log_msg(f"sys.argv = {str(sys.argv)}") + self.__params: Dict[str, Any] = urllib.parse.parse_qs(sys.argv[2][1:]) + + action = self.__params.get("action", None) + if action: + self.__action = action[0].lower() + log_msg(f"Set action to '{self.__action}'.") + + playlist_id = self.__params.get("playlistid", None) + if playlist_id: + self.__playlist_id = playlist_id[0] + owner_id = self.__params.get("ownerid", None) + if owner_id: + self.__owner_id = owner_id[0] + track_id = self.__params.get("trackid", None) + if track_id: + self.__track_id = track_id[0] + album_id = self.__params.get("albumid", None) + if album_id: + self.__album_id = album_id[0] + artist_id = self.__params.get("artistid", None) + if artist_id: + self.__artist_id = artist_id[0] + artist_name = self.__params.get("artistname", None) + if artist_name: + self.__artist_name = artist_name[0] + offset = self.__params.get("offset", None) + if offset: + self.__offset = int(offset[0]) + filt = self.__params.get("applyfilter", None) + if filt: + self.__filter = filt[0] + + def __get_authkey(self) -> str: + """get authentication key""" + auth_token = utils.get_cached_auth_token() + + if not auth_token: + msg = self.__addon.getLocalizedString(NO_CREDENTIALS_MSG_STR_ID) + dialog = xbmcgui.Dialog() + header = self.__addon.getAddonInfo("name") + dialog.ok(header, msg) + + return auth_token + + def __cache_checksum(self, opt_value: Any = None) -> str: + """simple cache checksum based on a few most important values""" + result = self.__cached_checksum + if not result: + # log_msg("__cached_checksum not found. Getting a new one.") + saved_tracks = self.__get_saved_track_ids() + saved_albums = self.__get_saved_album_ids() + followed_artists = self.__get_followed_artists() + generic_checksum = self.__addon.getSetting("cache_checksum") + result = ( + f"{len(saved_tracks)}-{len(saved_albums)}-{len(followed_artists)}" + f"-{generic_checksum}" + ) + self.__cached_checksum = result + # log_msg(f"New __cached_checksum = '{self.__cached_checksum}'.") + + if opt_value: + result += f"-{opt_value}" + + return result + + def __build_url(self, query: Dict[str, str]) -> str: + query_encoded = {} + for key, value in list(query.items()): + if isinstance(key, str): + key = key.encode("utf-8") + if isinstance(value, str): + value = value.encode("utf-8") + query_encoded[key] = value + + return self.__base_url + "?" + urllib.parse.urlencode(query_encoded) + + def delete_cache_db(self) -> None: + log_msg("Deleting plugin cache...") + simple_db_cache_addon = xbmcaddon.Addon(ADDON_ID) + db_path = simple_db_cache_addon.getAddonInfo("profile") + db_file = xbmcvfs.translatePath(f"{db_path}/simplecache.db") + os.remove(db_file) + log_msg(f"Deleted simplecache database file {db_file}.") + + dialog = xbmcgui.Dialog() + header = self.__addon.getAddonInfo("name") + msg = self.__addon.getLocalizedString(CACHED_CLEARED_STR_ID) + dialog.ok(header, msg) + + def refresh_listing(self) -> None: + self.__addon.setSetting("cache_checksum", time.strftime("%Y%m%d%H%M%S", time.gmtime())) + log_msg(f"New cache_checksum = \'{self.__addon.getSetting('cache_checksum')}\'") + xbmc.executebuiltin("Container.Refresh") + + def __add_track_listitems(self, tracks, append_artist_to_label: bool = False) -> None: + list_items = self.__get_track_list(tracks, append_artist_to_label) + xbmcplugin.addDirectoryItems(self.__addon_handle, list_items, totalItems=len(list_items)) + + @staticmethod + def __get_track_name(track, append_artist_to_label: bool) -> str: + if not append_artist_to_label: + return track["name"] + return f"{track['artist']} - {track['name']}" + + @staticmethod + def __get_track_rating(popularity: int) -> int: + if not popularity: + return 0 + + return int(math.ceil(popularity * 6 / 100.0)) - 1 + + def __get_track_list( + self, tracks, append_artist_to_label: bool = False + ) -> List[Tuple[str, xbmcgui.ListItem, bool]]: + list_items = [] + for count, track in enumerate(tracks): + list_items.append(self.__get_track_item(track, append_artist_to_label) + (False,)) + + return list_items + + def __get_track_item( + self, track: Dict[str, Any], append_artist_to_label: bool = False + ) -> Tuple[str, xbmcgui.ListItem]: + duration = track["duration_ms"] / 1000 + label = self.__get_track_name(track, append_artist_to_label) + title = label if self.append_artist_to_title else track["name"] + + # Local playback by using proxy on this machine. + url = f"http://localhost:{PROXY_PORT}/track/{track['id']}" + + # Testing Proxy + # url = f"http://localhost:8080/track/{track['id']}" + + log_msg(track['id']) + + li = xbmcgui.ListItem(label, offscreen=True) + li.setProperty("isPlayable", "true") + li.setInfo( + "music", + { + "title": title, + "genre": track["genre"], + "year": track["year"], + "tracknumber": track["track_number"], + "album": track["album"]["name"], + "artist": track["artist"], + "rating": track["rating"], + "duration": duration, + }, + ) + li.setArt({"thumb": track["thumb"]}) + li.setProperty("spotifytrackid", track["id"]) + li.setContentLookup(False) + li.addContextMenuItems(track["contextitems"], True) + li.setProperty("do_not_analyze", "true") + li.setMimeType("audio/ogg") + li.setProperty('mimetype', 'audio/ogg') + li.setProperty('inputstream', 'inputstream.ffmpegdirect') + li.setProperty('inputstream.ffmpegdirect.open_mode', 'curl') + li.setProperty('inputstream.ffmpegdirect.default_url', url) + + return url, li + + def __browse_main(self) -> None: + # Main listing. + xbmcplugin.setContent(self.__addon_handle, "files") + + items = [ + ( + self.__addon.getLocalizedString(MY_MUSIC_FOLDER_STR_ID), + f"plugin://{ADDON_ID}/?action={self.browse_main_library.__name__}", + MUSIC_LIBRARY_ICON, + True, + ), + ( + self.__addon.getLocalizedString(EXPLORE_STR_ID), + f"plugin://{ADDON_ID}/?action={self.browse_main_explore.__name__}", + MUSIC_EXPLORE_ICON, + True, + ), + ( + xbmc.getLocalizedString(KODI_SEARCH_STR_ID), + f"plugin://{ADDON_ID}/?action={self.search.__name__}", + MUSIC_SEARCH_ICON, + True, + ), + ( + self.__addon.getLocalizedString(CLEAR_CACHE_STR_ID), + f"plugin://{ADDON_ID}/?action={self.delete_cache_db.__name__}", + CLEAR_CACHE_ICON, + False, + ), + ] + + for item in items: + li = xbmcgui.ListItem(item[0], path=item[1]) + li.setProperty("IsPlayable", "false") + li.setArt({"icon": os.path.join(self.__addon_icon_path, item[2])}) + li.addContextMenuItems([], True) + xbmcplugin.addDirectoryItem( + handle=self.__addon_handle, url=item[1], listitem=li, isFolder=item[3] + ) + + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + + def browse_main_library(self) -> None: + # Library nodes. + xbmcplugin.setContent(self.__addon_handle, "files") + xbmcplugin.setProperty( + self.__addon_handle, + "FolderName", + self.__addon.getLocalizedString(MY_MUSIC_FOLDER_STR_ID), + ) + + items = [ + ( + xbmc.getLocalizedString(KODI_PLAYLISTS_STR_ID), + f"plugin://{ADDON_ID}/" + f"?action={self.browse_playlists.__name__}&ownerid={self.__userid}", + MUSIC_PLAYLISTS_ICON, + ), + ( + xbmc.getLocalizedString(KODI_ALBUMS_STR_ID), + f"plugin://{ADDON_ID}/?action={self.browse_saved_albums.__name__}", + MUSIC_ALBUMS_ICON, + ), + ( + xbmc.getLocalizedString(KODI_SONGS_STR_ID), + f"plugin://{ADDON_ID}/?action={self.browse_saved_tracks.__name__}", + MUSIC_SONGS_ICON, + ), + ( + xbmc.getLocalizedString(KODI_ARTISTS_STR_ID), + f"plugin://{ADDON_ID}/?action={self.browse_saved_artists.__name__}", + MUSIC_ARTISTS_ICON, + ), + ( + self.__addon.getLocalizedString(FOLLOWED_ARTISTS_STR_ID), + f"plugin://{ADDON_ID}/?action={self.browse_followed_artists.__name__}", + MUSIC_ARTISTS_ICON, + ), + ( + self.__addon.getLocalizedString(MOST_PLAYED_ARTISTS_STR_ID), + f"plugin://{ADDON_ID}/?action={self.browse_top_artists.__name__}", + MUSIC_TOP_ARTISTS_ICON, + ), + ( + self.__addon.getLocalizedString(MOST_PLAYED_TRACKS_STR_ID), + f"plugin://{ADDON_ID}/?action={self.browse_top_tracks.__name__}", + MUSIC_TOP_TRACKS_ICON, + ), + ] + + for item in items: + li = xbmcgui.ListItem(item[0], path=item[1]) + li.setProperty("do_not_analyze", "true") + li.setProperty("IsPlayable", "false") + li.setArt({"icon": os.path.join(self.__addon_icon_path, item[2])}) + li.addContextMenuItems([], True) + xbmcplugin.addDirectoryItem( + handle=self.__addon_handle, url=item[1], listitem=li, isFolder=True + ) + + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + + def browse_top_artists(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "artists") + result = self.__spotipy.current_user_top_artists(limit=20, offset=0) + + cache_str = f"spotify.topartists.{self.__userid}" + checksum = self.__cache_checksum(result["total"]) + items = self.cache.get(cache_str, checksum=checksum) + if not items: + count = len(result["items"]) + while result["total"] > count: + result["items"] += self.__spotipy.current_user_top_artists(limit=20, offset=count)[ + "items" + ] + count += 50 + items = self.__prepare_artist_listitems(result["items"]) + self.cache.set(cache_str, items, checksum=checksum) + self.__add_artist_listitems(items) + + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_artists: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_artists})") + + def browse_top_tracks(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "songs") + results = self.__spotipy.current_user_top_tracks(limit=20, offset=0) + + cache_str = f"spotify.toptracks.{self.__userid}" + checksum = self.__cache_checksum(results["total"]) + tracks = self.cache.get(cache_str, checksum=checksum) + if not tracks: + tracks = results["items"] + while results["next"]: + results = self.__spotipy.next(results) + tracks.extend(results["items"]) + tracks = self.__prepare_track_listitems(tracks=tracks) + self.cache.set(cache_str, tracks, checksum=checksum) + self.__add_track_listitems(tracks, True) + + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_songs: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_songs})") + + def __get_explore_categories(self) -> List[Tuple[Any, str, Union[str, Any]]]: + items = [] + + categories = self.__spotipy.categories( + country=self.__user_country, limit=50, locale=self.__user_country + ) + count = len(categories["categories"]["items"]) + while categories["categories"]["total"] > count: + categories["categories"]["items"] += self.__spotipy.categories( + country=self.__user_country, limit=50, offset=count, locale=self.__user_country + )["categories"]["items"] + count += 50 + + for item in categories["categories"]["items"]: + thumb = "DefaultMusicGenre.png" + for icon in item["icons"]: + thumb = icon["url"] + break + items.append( + ( + item["name"], + f"plugin://{ADDON_ID}/" + f"?action={self.browse_category.__name__}&applyfilter={item['id']}", + thumb, + ) + ) + + return items + + def browse_main_explore(self) -> None: + # Explore nodes. + xbmcplugin.setContent(self.__addon_handle, "files") + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", self.__addon.getLocalizedString(EXPLORE_STR_ID) + ) + items = [ + ( + self.__addon.getLocalizedString(FEATURED_PLAYLISTS_STR_ID), + f"plugin://{ADDON_ID}/" + f"?action={self.browse_playlists.__name__}&applyfilter=featured", + MUSIC_PLAYLISTS_ICON, + ), + ( + self.__addon.getLocalizedString(BROWSE_NEW_RELEASES_STR_ID), + f"plugin://{ADDON_ID}/?action={self.browse_new_releases.__name__}", + MUSIC_ALBUMS_ICON, + ), + ] + + # Add categories. + items += self.__get_explore_categories() + for item in items: + li = xbmcgui.ListItem(item[0], path=item[1]) + li.setProperty("do_not_analyze", "true") + li.setProperty("IsPlayable", "false") + li.setArt({"icon": os.path.join(self.__addon_icon_path, item[2])}) + li.addContextMenuItems([], True) + xbmcplugin.addDirectoryItem( + handle=self.__addon_handle, url=item[1], listitem=li, isFolder=True + ) + + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + + def __get_album_tracks(self, album: Dict[str, Any]) -> List[Dict[str, Any]]: + cache_str = f"spotify.albumtracks{album['id']}" + checksum = self.__cache_checksum() + + album_tracks = self.cache.get(cache_str, checksum=checksum) + if not album_tracks: + track_ids = [] + count = 0 + while album["tracks"]["total"] > count: + tracks = self.__spotipy.album_tracks( + album["id"], market=self.__user_country, limit=50, offset=count + )["items"] + for track in tracks: + track_ids.append(track["id"]) + count += 50 + album_tracks = self.__prepare_track_listitems(track_ids, album_details=album) + self.cache.set(cache_str, album_tracks, checksum=checksum) + + return album_tracks + + def browse_album(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "songs") + album = self.__spotipy.album(self.__album_id, market=self.__user_country) + xbmcplugin.setProperty(self.__addon_handle, "FolderName", album["name"]) + tracks = self.__get_album_tracks(album) + if album.get("album_type") == "compilation": + self.__add_track_listitems(tracks, True) + else: + self.__add_track_listitems(tracks) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_TRACKNUM) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_TITLE) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_SONG_RATING) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_ARTIST) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_songs: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_songs})") + + def artist_top_tracks(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "songs") + xbmcplugin.setProperty( + self.__addon_handle, + "FolderName", + self.__addon.getLocalizedString(ARTIST_TOP_TRACKS_STR_ID), + ) + tracks = self.__spotipy.artist_top_tracks(self.__artist_id, country=self.__user_country) + tracks = self.__prepare_track_listitems(tracks=tracks["tracks"]) + self.__add_track_listitems(tracks) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_TRACKNUM) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_TITLE) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_SONG_RATING) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_songs: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_songs})") + + def related_artists(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "artists") + xbmcplugin.setProperty( + self.__addon_handle, + "FolderName", + self.__addon.getLocalizedString(RELATED_ARTISTS_STR_ID), + ) + cache_str = f"spotify.relatedartists.{self.__artist_id}" + checksum = self.__cache_checksum() + artists = self.cache.get(cache_str, checksum=checksum) + if not artists: + artists = self.__spotipy.artist_related_artists(self.__artist_id) + artists = self.__prepare_artist_listitems(artists["artists"]) + self.cache.set(cache_str, artists, checksum=checksum) + self.__add_artist_listitems(artists) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_artists: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_artists})") + + def __get_playlist_details(self, playlist_id: str) -> Playlist: + playlist = self.__spotipy.playlist( + playlist_id, fields="tracks(total),name,owner(id),id", market=self.__user_country + ) + # Get from cache first. + cache_str = f"spotify.playlistdetails.{playlist['id']}" + checksum = self.__cache_checksum(playlist["tracks"]["total"]) + # log_msg(f"Playlist cache_str = '{cache_str}', checksum = '{checksum}'.") + playlist_details = self.cache.get(cache_str, checksum=checksum) + if not playlist_details: + # Get listing from api. + count = 0 + playlist_details = playlist + playlist_details["tracks"]["items"] = [] + while playlist["tracks"]["total"] > count: + playlist_details["tracks"]["items"] += self.__spotipy.user_playlist_tracks( + playlist["owner"]["id"], + playlist["id"], + market=self.__user_country, + fields="", + limit=50, + offset=count, + )["items"] + count += 50 + playlist_details["tracks"]["items"] = self.__prepare_track_listitems( + tracks=playlist_details["tracks"]["items"], playlist_details=playlist + ) + # log_msg(f"playlist_details = {playlist_details}") + checksum = self.__cache_checksum(playlist["tracks"]["total"]) + self.cache.set(cache_str, playlist_details, checksum=checksum) + # log_msg(f"Got new playlist - checksum = '{checksum}'") + + return playlist_details + + def browse_playlist(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "songs") + playlist_details = self.__get_playlist_details(self.__playlist_id) + xbmcplugin.setProperty(self.__addon_handle, "FolderName", playlist_details["name"]) + self.__add_track_listitems(playlist_details["tracks"]["items"], True) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_songs: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_songs})") + + def play_playlist(self) -> None: + """play entire playlist""" + playlist_details = self.__get_playlist_details(self.__playlist_id) + log_msg(f"Start playing playlist '{playlist_details['name']}'.") + + kodi_playlist = xbmc.PlayList(0) + kodi_playlist.clear() + + def add_to_playlist(trk) -> None: + url, li = self.__get_track_item(trk, True) + kodi_playlist.add(url, li) + + # Add first track and start playing. + add_to_playlist(playlist_details["tracks"]["items"][0]) + kodi_player = xbmc.Player() + kodi_player.play(kodi_playlist) + + # Add remaining tracks to the playlist while already playing. + for track in playlist_details["tracks"]["items"][1:]: + add_to_playlist(track) + + def __get_category(self, categoryid: str) -> Playlist: + category = self.__spotipy.category( + categoryid, country=self.__user_country, locale=self.__user_country + ) + playlists = self.__spotipy.category_playlists( + categoryid, country=self.__user_country, limit=50, offset=0 + ) + playlists["category"] = category["name"] + count = len(playlists["playlists"]["items"]) + while playlists["playlists"]["total"] > count: + playlists["playlists"]["items"] += self.__spotipy.category_playlists( + categoryid, country=self.__user_country, limit=50, offset=count + )["playlists"]["items"] + count += 50 + playlists["playlists"]["items"] = self.__prepare_playlist_listitems( + playlists["playlists"]["items"] + ) + + return playlists + + def browse_category(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "files") + playlists = self.__get_category(self.__filter) + self.__add_playlist_listitems(playlists["playlists"]["items"]) + xbmcplugin.setProperty(self.__addon_handle, "FolderName", playlists["category"]) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_category: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_category})") + + def follow_playlist(self) -> None: + self.__spotipy.current_user_follow_playlist(self.__playlist_id) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + self.refresh_listing() + + def add_track_to_playlist(self) -> None: + xbmc.executebuiltin("ActivateWindow(busydialog)") + + if not self.__track_id and xbmc.getInfoLabel("MusicPlayer.(1).Property(spotifytrackid)"): + self.__track_id = xbmc.getInfoLabel("MusicPlayer.(1).Property(spotifytrackid)") + + own_playlists, own_playlist_names = utils.get_user_playlists(self.__spotipy, 50) + own_playlist_names.append(xbmc.getLocalizedString(KODI_NEW_PLAYLIST_STR_ID)) + + xbmc.executebuiltin("Dialog.Close(busydialog)") + select = xbmcgui.Dialog().select( + xbmc.getLocalizedString(KODI_SELECT_PLAYLIST_STR_ID), own_playlist_names + ) + if select != -1 and own_playlist_names[select] == xbmc.getLocalizedString( + KODI_NEW_PLAYLIST_STR_ID + ): + # create new playlist... + kb = xbmc.Keyboard("", xbmc.getLocalizedString(KODI_ENTER_NEW_PLAYLIST_STR_ID)) + kb.setHiddenInput(False) + kb.doModal() + if kb.isConfirmed(): + name = kb.getText() + playlist = self.__spotipy.user_playlist_create(self.__userid, name, False) + self.__spotipy.playlist_add_items(playlist["id"], [self.__track_id]) + elif select != -1: + playlist = own_playlists[select] + self.__spotipy.playlist_add_items(playlist["id"], [self.__track_id]) + + def remove_track_from_playlist(self) -> None: + self.__spotipy.playlist_remove_all_occurrences_of_items( + self.__playlist_id, [self.__track_id] + ) + self.refresh_listing() + + def unfollow_playlist(self) -> None: + self.__spotipy.current_user_unfollow_playlist(self.__playlist_id) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + self.refresh_listing() + + def follow_artist(self) -> None: + self.__spotipy.user_follow_artists([self.__artist_id]) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + self.refresh_listing() + + def unfollow_artist(self) -> None: + self.__spotipy.user_unfollow_artists([self.__artist_id]) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + self.refresh_listing() + + def save_album(self) -> None: + self.__spotipy.current_user_saved_albums_add([self.__album_id]) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + self.refresh_listing() + + def remove_album(self) -> None: + self.__spotipy.current_user_saved_albums_delete([self.__album_id]) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + self.refresh_listing() + + def save_track(self) -> None: + self.__spotipy.current_user_saved_tracks_add([self.__track_id]) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + self.refresh_listing() + + def remove_track(self) -> None: + self.__spotipy.current_user_saved_tracks_delete([self.__track_id]) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + self.refresh_listing() + + def __get_featured_playlists(self) -> Playlist: + playlists = self.__spotipy.featured_playlists( + country=self.__user_country, limit=50, offset=0 + ) + count = len(playlists["playlists"]["items"]) + total = playlists["playlists"]["total"] + while total > count: + playlists["playlists"]["items"] += self.__spotipy.featured_playlists( + country=self.__user_country, limit=50, offset=count + )["playlists"]["items"] + count += 50 + playlists["playlists"]["items"] = self.__prepare_playlist_listitems( + playlists["playlists"]["items"] + ) + + return playlists + + def __get_user_playlists(self, userid): + playlists = self.__spotipy.user_playlists(userid, limit=1, offset=0) + count = len(playlists["items"]) + total = playlists["total"] + cache_str = f"spotify.userplaylists.{userid}" + checksum = self.__cache_checksum(total) + + cache = self.cache.get(cache_str, checksum=checksum) + if cache: + playlists = cache + else: + while total > count: + playlists["items"] += self.__spotipy.user_playlists(userid, limit=50, offset=count)[ + "items" + ] + count += 50 + playlists = self.__prepare_playlist_listitems(playlists["items"]) + self.cache.set(cache_str, playlists, checksum=checksum) + + return playlists + + def __get_curuser_playlistids(self) -> List[str]: + playlists = self.__spotipy.current_user_playlists(limit=1, offset=0) + count = len(playlists["items"]) + total = playlists["total"] + cache_str = f"spotify.userplaylistids.{self.__userid}" + playlist_ids = self.cache.get(cache_str, checksum=total) + if not playlist_ids: + playlist_ids = [] + while total > count: + playlists["items"] += self.__spotipy.current_user_playlists(limit=50, offset=count)[ + "items" + ] + count += 50 + for playlist in playlists["items"]: + playlist_ids.append(playlist["id"]) + self.cache.set(cache_str, playlist_ids, checksum=total) + return playlist_ids + + def browse_playlists(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "files") + if self.__filter == "featured": + playlists = self.__get_featured_playlists() + xbmcplugin.setProperty(self.__addon_handle, "FolderName", playlists["message"]) + playlists = playlists["playlists"]["items"] + else: + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", xbmc.getLocalizedString(KODI_PLAYLISTS_STR_ID) + ) + playlists = self.__get_user_playlists(self.__owner_id) + + self.__add_playlist_listitems(playlists) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_playlists: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_playlists})") + + def __get_new_releases(self): + albums = self.__spotipy.new_releases(country=self.__user_country, limit=50, offset=0) + count = len(albums["albums"]["items"]) + while albums["albums"]["total"] > count: + albums["albums"]["items"] += self.__spotipy.new_releases( + country=self.__user_country, limit=50, offset=count + )["albums"]["items"] + count += 50 + + album_ids = [] + for album in albums["albums"]["items"]: + album_ids.append(album["id"]) + albums = self.__prepare_album_listitems(album_ids) + + return albums + + def browse_new_releases(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "albums") + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", self.__addon.getLocalizedString(NEW_RELEASES_STR_ID) + ) + albums = self.__get_new_releases() + self.__add_album_listitems(albums) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_albums: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_albums})") + + def __prepare_track_listitems( + self, track_ids=None, tracks=None, playlist_details=None, album_details=None + ) -> List[Dict[str, Any]]: + if tracks is None: + tracks = [] + if track_ids is None: + track_ids = [] + + new_tracks: List[Dict[str, Any]] = [] + + # For tracks, we always get the full details unless full tracks already supplied. + if track_ids and not tracks: + for chunk in get_chunks(track_ids, 20): + tracks += self.__spotipy.tracks(chunk, market=self.__user_country)["tracks"] + + saved_track_ids = self.__get_saved_track_ids() + + followed_artists = [] + for artist in self.__get_followed_artists(): + followed_artists.append(artist["id"]) + + for track in tracks: + if track.get("track"): + track = track["track"] + if album_details: + track["album"] = album_details + if track.get("images"): + thumb = track["images"][0]["url"] + elif track.get("album", {}).get("images"): + thumb = track["album"]["images"][0]["url"] + else: + thumb = "DefaultMusicSongs.png" + track["thumb"] = thumb + + # skip local tracks in playlists + if not track.get("id"): + continue + + artists = [] + for artist in track["artists"]: + artists.append(artist["name"]) + track["artist"] = " / ".join(artists) + track["artistid"] = track["artists"][0]["id"] + + track["genre"] = " / ".join(track["album"].get("genres", [])) + + # Allow for 'release_date' being empty. + release_date = "0" if "album" not in track else track["album"].get("release_date", "0") + track["year"] = ( + 1900 + if not release_date + else int(track["album"].get("release_date", "0").split("-")[0]) + ) + + track["rating"] = str(self.__get_track_rating(track["popularity"])) + if playlist_details: + track["playlistid"] = playlist_details["id"] + + # Use original track id for actions when the track was relinked. + if track.get("linked_from"): + real_track_id = track["linked_from"]["id"] + real_track_uri = track["linked_from"]["uri"] + else: + real_track_id = track["id"] + real_track_uri = track["uri"] + + contextitems = [] + if track["id"] in saved_track_ids: + contextitems.append( + ( + self.__addon.getLocalizedString(REMOVE_TRACKS_FROM_MY_MUSIC_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.remove_track.__name__}&trackid={real_track_id})", + ) + ) + else: + contextitems.append( + ( + self.__addon.getLocalizedString(SAVE_TRACKS_TO_MY_MUSIC_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.save_track.__name__}&trackid={real_track_id})", + ) + ) + + if playlist_details and playlist_details["owner"]["id"] == self.__userid: + contextitems.append( + ( + f"{self.__addon.getLocalizedString(REMOVE_FROM_PLAYLIST_STR_ID)}" + f" {playlist_details['name']}", + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.remove_track_from_playlist.__name__}&trackid=" + f"{real_track_uri}&playlistid={playlist_details['id']})", + ) + ) + + contextitems.append( + ( + xbmc.getLocalizedString(KODI_ADD_TO_PLAYLIST_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.add_track_to_playlist.__name__}&trackid={real_track_uri})", + ) + ) + + contextitems.append( + ( + self.__addon.getLocalizedString(ARTIST_TOP_TRACKS_STR_ID), + f"Container.Update(plugin://{ADDON_ID}/" + f"?action={self.artist_top_tracks.__name__}&artistid={track['artistid']})", + ) + ) + contextitems.append( + ( + self.__addon.getLocalizedString(RELATED_ARTISTS_STR_ID), + f"Container.Update(plugin://{ADDON_ID}/" + f"?action={self.related_artists.__name__}&artistid={track['artistid']})", + ) + ) + contextitems.append( + ( + self.__addon.getLocalizedString(ALL_ALBUMS_FOR_ARTIST_STR_ID), + f"Container.Update(plugin://{ADDON_ID}/" + f"?action={self.browse_artist_albums.__name__}&artistid={track['artistid']})", + ) + ) + + if track["artistid"] in followed_artists: + # unfollow artist + contextitems.append( + ( + self.__addon.getLocalizedString(UNFOLLOW_ARTIST_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.unfollow_artist.__name__}&artistid={track['artistid']})", + ) + ) + else: + # follow artist + contextitems.append( + ( + self.__addon.getLocalizedString(FOLLOW_ARTIST_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.follow_artist.__name__}&artistid={track['artistid']})", + ) + ) + + contextitems.append( + ( + self.__addon.getLocalizedString(REFRESH_LISTING_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" f"?action={self.refresh_listing.__name__})", + ) + ) + track["contextitems"] = contextitems + new_tracks.append(track) + + return new_tracks + + def __prepare_album_listitems( + self, album_ids: List[str] = None, albums: List[Dict[str, Any]] = None + ) -> List[Dict[str, Any]]: + if albums is None: + albums: List[Dict[str, Any]] = [] + if album_ids is None: + album_ids = [] + if not albums and album_ids: + # Get full info in chunks of 20. + for chunk in get_chunks(album_ids, 20): + albums += self.__spotipy.albums(chunk, market=self.__user_country)["albums"] + + saved_albums = self.__get_saved_album_ids() + + # process listing + for track in albums: + if track.get("images"): + track["thumb"] = track["images"][0]["url"] + else: + track["thumb"] = "DefaultMusicAlbums.png" + + track["url"] = self.__build_url( + {"action": self.browse_album.__name__, "albumid": track["id"]} + ) + + artists = [] + for artist in track["artists"]: + artists.append(artist["name"]) + track["artist"] = " / ".join(artists) + track["genre"] = " / ".join(track["genres"]) + track["year"] = int(track["release_date"].split("-")[0]) + track["rating"] = str(self.__get_track_rating(track["popularity"])) + track["artistid"] = track["artists"][0]["id"] + + contextitems = [ + (xbmc.getLocalizedString(KODI_BROWSE_STR_ID), f"RunPlugin({track['url']})"), + ( + self.__addon.getLocalizedString(ARTIST_TOP_TRACKS_STR_ID), + f"Container.Update(plugin://{ADDON_ID}/" + f"?action={self.artist_top_tracks.__name__}&artistid={track['artistid']})", + ), + ( + self.__addon.getLocalizedString(RELATED_ARTISTS_STR_ID), + f"Container.Update(plugin://{ADDON_ID}/" + f"?action={self.related_artists.__name__}&artistid={track['artistid']})", + ), + ( + self.__addon.getLocalizedString(ALL_ALBUMS_FOR_ARTIST_STR_ID), + f"Container.Update(plugin://{ADDON_ID}/" + f"?action={self.browse_artist_albums.__name__}&artistid={track['artistid']})", + ), + ( + self.__addon.getLocalizedString(REFRESH_LISTING_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" f"?action={self.refresh_listing.__name__})", + ), + ] + + if track["id"] in saved_albums: + contextitems.append( + ( + self.__addon.getLocalizedString(REMOVE_TRACKS_FROM_MY_MUSIC_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.remove_album.__name__}&albumid={track['id']})", + ) + ) + else: + contextitems.append( + ( + self.__addon.getLocalizedString(SAVE_TRACKS_TO_MY_MUSIC_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.save_album.__name__}&albumid={track['id']})", + ) + ) + + track["contextitems"] = contextitems + + return albums + + def __add_album_listitems( + self, albums: List[Dict[str, Any]], append_artist_to_label: bool = False + ) -> None: + # Process listing. + for track in albums: + label = self.__get_track_name(track, append_artist_to_label) + + li = xbmcgui.ListItem(label, path=track["url"], offscreen=True) + info_labels = { + "title": track["name"], + "genre": track["genre"], + "year": track["year"], + "album": track["name"], + "artist": track["artist"], + "rating": track["rating"], + } + li.setInfo(type="Music", infoLabels=info_labels) + li.setArt({"thumb": track["thumb"]}) + li.setProperty("do_not_analyze", "true") + li.setProperty("IsPlayable", "false") + li.addContextMenuItems(track["contextitems"], True) + xbmcplugin.addDirectoryItem( + handle=self.__addon_handle, url=track["url"], listitem=li, isFolder=True + ) + + def __prepare_artist_listitems( + self, artists: List[Dict[str, Any]], is_followed: bool = False + ) -> List[Dict[str, Any]]: + followed_artists = [] + if not is_followed: + for artist in self.__get_followed_artists(): + followed_artists.append(artist["id"]) + + for item in artists: + if not item: + return [] + if item.get("artist"): + item = item["artist"] + if item.get("images"): + item["thumb"] = item["images"][0]["url"] + else: + item["thumb"] = "DefaultMusicArtists.png" + + item["url"] = self.__build_url( + {"action": self.browse_artist_albums.__name__, "artistid": item["id"]} + ) + + item["genre"] = " / ".join(item["genres"]) + item["rating"] = str(self.__get_track_rating(item["popularity"])) + item["followerslabel"] = f"{item['followers']['total']} followers" + + contextitems = [ + (xbmc.getLocalizedString(KODI_ALBUMS_STR_ID), f"Container.Update({item['url']})"), + ( + self.__addon.getLocalizedString(ARTIST_TOP_TRACKS_STR_ID), + f"Container.Update(plugin://{ADDON_ID}/" + f"?action={self.artist_top_tracks.__name__}&artistid={item['id']})", + ), + ( + self.__addon.getLocalizedString(RELATED_ARTISTS_STR_ID), + f"Container.Update(plugin://{ADDON_ID}/" + f"?action={self.related_artists.__name__}&artistid={item['id']})", + ), + ] + + if is_followed or item["id"] in followed_artists: + contextitems.append( + ( + self.__addon.getLocalizedString(UNFOLLOW_ARTIST_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.unfollow_artist.__name__}&artistid={item['id']})", + ) + ) + else: + contextitems.append( + ( + self.__addon.getLocalizedString(FOLLOW_ARTIST_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.follow_artist.__name__}&artistid={item['id']})", + ) + ) + + item["contextitems"] = contextitems + + return artists + + def __add_artist_listitems(self, artists: List[Dict[str, Any]]) -> None: + for item in artists: + li = xbmcgui.ListItem(item["name"], path=item["url"], offscreen=True) + info_labels = { + "title": item["name"], + "genre": item["genre"], + "artist": item["name"], + "rating": item["rating"], + } + li.setInfo(type="Music", infoLabels=info_labels) + li.setArt({"thumb": item["thumb"]}) + li.setProperty("do_not_analyze", "true") + li.setProperty("IsPlayable", "false") + li.setLabel2(item["followerslabel"]) + li.addContextMenuItems(item["contextitems"], True) + xbmcplugin.addDirectoryItem( + handle=self.__addon_handle, + url=item["url"], + listitem=li, + isFolder=True, + totalItems=len(artists), + ) + + def __prepare_playlist_listitems(self, playlists: List[Dict[str, Any]]) -> List[Dict[str, Any]]: + playlists2 = [] + followed_playlists = self.__get_curuser_playlistids() + + for item in playlists: + if not item: + continue + + if item.get("images"): + item["thumb"] = item["images"][0]["url"] + else: + item["thumb"] = "DefaultMusicAlbums.png" + + item["url"] = self.__build_url( + { + "action": self.browse_playlist.__name__, + "playlistid": item["id"], + "ownerid": item["owner"]["id"], + } + ) + + contextitems = [ + ( + xbmc.getLocalizedString(KODI_PLAY_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.play_playlist.__name__}&playlistid={item['id']}" + f"&ownerid={item['owner']['id']})", + ), + ( + self.__addon.getLocalizedString(REFRESH_LISTING_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" f"?action={self.refresh_listing.__name__})", + ), + ] + + if item["owner"]["id"] != self.__userid and item["id"] in followed_playlists: + contextitems.append( + ( + self.__addon.getLocalizedString(UNFOLLOW_PLAYLIST_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.unfollow_playlist.__name__}&playlistid={item['id']}" + f"&ownerid={item['owner']['id']})", + ) + ) + elif item["owner"]["id"] != self.__userid: + contextitems.append( + ( + self.__addon.getLocalizedString(FOLLOW_PLAYLIST_STR_ID), + f"RunPlugin(plugin://{ADDON_ID}/" + f"?action={self.follow_playlist.__name__}&playlistid={item['id']}" + f"&ownerid={item['owner']['id']})", + ) + ) + + item["contextitems"] = contextitems + playlists2.append(item) + + return playlists2 + + def __add_playlist_listitems(self, playlists: List[Dict[str, Any]]) -> None: + for item in playlists: + li = xbmcgui.ListItem(item["name"], path=item["url"], offscreen=True) + li.setProperty("do_not_analyze", "true") + li.setProperty("IsPlayable", "false") + + li.addContextMenuItems(item["contextitems"], True) + li.setArt( + { + "fanart": os.path.join(self.__addon_icon_path, "fanart.jpg"), + "thumb": item["thumb"], + } + ) + xbmcplugin.addDirectoryItem( + handle=self.__addon_handle, url=item["url"], listitem=li, isFolder=True + ) + + def browse_artist_albums(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "albums") + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", xbmc.getLocalizedString(KODI_ALBUMS_STR_ID) + ) + artist_albums = self.__spotipy.artist_albums( + self.__artist_id, + album_type="album,single,compilation", + country=self.__user_country, + limit=50, + offset=0, + ) + count = len(artist_albums["items"]) + albumids = [] + while artist_albums["total"] > count: + artist_albums["items"] += self.__spotipy.artist_albums( + self.__artist_id, + album_type="album,single,compilation", + country=self.__user_country, + limit=50, + offset=count, + )["items"] + count += 50 + for album in artist_albums["items"]: + albumids.append(album["id"]) + albums = self.__prepare_album_listitems(albumids) + self.__add_album_listitems(albums) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_ALBUM_IGNORE_THE) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_SONG_RATING) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_albums: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_albums})") + + def __get_saved_album_ids(self) -> List[str]: + albums = self.__spotipy.current_user_saved_albums(limit=1, offset=0) + cache_str = f"spotify-savedalbumids.{self.__userid}" + checksum = albums["total"] + cache = self.cache.get(cache_str, checksum=checksum) + if cache: + return cache + + album_ids = [] + if albums and albums.get("items"): + count = len(albums["items"]) + album_ids = [] + while albums["total"] > count: + albums["items"] += self.__spotipy.current_user_saved_albums(limit=50, offset=count)[ + "items" + ] + count += 50 + for album in albums["items"]: + album_ids.append(album["album"]["id"]) + self.cache.set(cache_str, album_ids, checksum=checksum) + + return album_ids + + def __get_saved_albums(self) -> List[Dict[str, Any]]: + album_ids = self.__get_saved_album_ids() + cache_str = f"spotify.savedalbums.{self.__userid}" + checksum = self.__cache_checksum(len(album_ids)) + albums = self.cache.get(cache_str, checksum=checksum) + if not albums: + albums = self.__prepare_album_listitems(album_ids) + self.cache.set(cache_str, albums, checksum=checksum) + return albums + + def browse_saved_albums(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "albums") + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", xbmc.getLocalizedString(KODI_ALBUMS_STR_ID) + ) + albums = self.__get_saved_albums() + self.__add_album_listitems(albums, True) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_ALBUM_IGNORE_THE) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_VIDEO_YEAR) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_SONG_RATING) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + xbmcplugin.setContent(self.__addon_handle, "albums") + if self.default_view_albums: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_albums})") + + def __get_saved_track_ids(self) -> List[str]: + saved_tracks = self.__spotipy.current_user_saved_tracks( + limit=1, offset=self.__offset, market=self.__user_country + ) + total = saved_tracks["total"] + cache_str = f"spotify.savedtracksids.{self.__userid}" + cache = self.cache.get(cache_str, checksum=total) + if cache: + return cache + + # Get from api. + track_ids = [] + count = len(saved_tracks["items"]) + while total > count: + saved_tracks["items"] += self.__spotipy.current_user_saved_tracks( + limit=50, offset=count, market=self.__user_country + )["items"] + count += 50 + for track in saved_tracks["items"]: + track_ids.append(track["track"]["id"]) + self.cache.set(cache_str, track_ids, checksum=total) + + return track_ids + + def __get_saved_tracks(self): + # Get from cache first. + track_ids = self.__get_saved_track_ids() + cache_str = f"spotify.savedtracks.{self.__userid}" + + tracks = self.cache.get(cache_str, checksum=len(track_ids)) + if not tracks: + # Get from api. + tracks = self.__prepare_track_listitems(track_ids) + self.cache.set(cache_str, tracks, checksum=len(track_ids)) + + return tracks + + def browse_saved_tracks(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "songs") + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", xbmc.getLocalizedString(KODI_SONGS_STR_ID) + ) + tracks = self.__get_saved_tracks() + self.__add_track_listitems(tracks, True) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_songs: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_songs})") + + def __get_saved_artists(self) -> List[Dict[str, Any]]: + saved_albums = self.__get_saved_albums() + followed_artists = self.__get_followed_artists() + cache_str = f"spotify.savedartists.{self.__userid}" + checksum = len(saved_albums) + len(followed_artists) + artists = self.cache.get(cache_str, checksum=checksum) + if not artists: + all_artist_ids = [] + artists = [] + # extract the artists from all saved albums + for item in saved_albums: + for artist in item["artists"]: + if artist["id"] not in all_artist_ids: + all_artist_ids.append(artist["id"]) + for chunk in get_chunks(all_artist_ids, 50): + artists += self.__prepare_artist_listitems(self.__spotipy.artists(chunk)["artists"]) + # append artists that are followed + for artist in followed_artists: + if not artist["id"] in all_artist_ids: + artists.append(artist) + self.cache.set(cache_str, artists, checksum=checksum) + + return artists + + def browse_saved_artists(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "artists") + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", xbmc.getLocalizedString(KODI_ARTISTS_STR_ID) + ) + artists = self.__get_saved_artists() + self.__add_artist_listitems(artists) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_TITLE) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_artists: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_artists})") + + def __get_followed_artists(self) -> List[Dict[str, Any]]: + artists = self.__spotipy.current_user_followed_artists(limit=50) + cache_str = f"spotify.followedartists.{self.__userid}" + checksum = artists["artists"]["total"] + + cache = self.cache.get(cache_str, checksum=checksum) + if cache: + artists = cache + else: + count = len(artists["artists"]["items"]) + after = artists["artists"]["cursors"]["after"] + while artists["artists"]["total"] > count: + result = self.__spotipy.current_user_followed_artists(limit=50, after=after) + artists["artists"]["items"] += result["artists"]["items"] + after = result["artists"]["cursors"]["after"] + count += 50 + artists = self.__prepare_artist_listitems(artists["artists"]["items"], is_followed=True) + self.cache.set(cache_str, artists, checksum=checksum) + + return artists + + def browse_followed_artists(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "artists") + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", xbmc.getLocalizedString(KODI_ARTISTS_STR_ID) + ) + artists = self.__get_followed_artists() + self.__add_artist_listitems(artists) + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_TITLE) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + if self.default_view_artists: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_artists})") + + def search_artists(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "artists") + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", xbmc.getLocalizedString(KODI_ARTISTS_STR_ID) + ) + + result = self.__spotipy.search( + q=f"artist:{self.__artist_id}", + type="artist", + limit=self.__limit, + offset=self.__offset, + market=self.__user_country, + ) + + artists = self.__prepare_artist_listitems(result["artists"]["items"]) + self.__add_artist_listitems(artists) + self.__add_next_button(result["artists"]["total"]) + + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + + if self.default_view_artists: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_artists})") + + def search_tracks(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "songs") + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", xbmc.getLocalizedString(KODI_SONGS_STR_ID) + ) + + result = self.__spotipy.search( + q=f"track:{self.__track_id}", + type="track", + limit=self.__limit, + offset=self.__offset, + market=self.__user_country, + ) + + tracks = self.__prepare_track_listitems(tracks=result["tracks"]["items"]) + self.__add_track_listitems(tracks, True) + self.__add_next_button(result["tracks"]["total"]) + + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + + if self.default_view_songs: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_songs})") + + def search_albums(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "albums") + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", xbmc.getLocalizedString(KODI_ALBUMS_STR_ID) + ) + + result = self.__spotipy.search( + q=f"album:{self.__album_id}", + type="album", + limit=self.__limit, + offset=self.__offset, + market=self.__user_country, + ) + + album_ids = [] + for album in result["albums"]["items"]: + album_ids.append(album["id"]) + albums = self.__prepare_album_listitems(album_ids) + self.__add_album_listitems(albums, True) + self.__add_next_button(result["albums"]["total"]) + + xbmcplugin.addSortMethod(self.__addon_handle, xbmcplugin.SORT_METHOD_UNSORTED) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + + if self.default_view_albums: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_albums})") + + def search_playlists(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "files") + + result = self.__spotipy.search( + q=self.__playlist_id, + type="playlist", + limit=self.__limit, + offset=self.__offset, + market=self.__user_country, + ) + + log_msg(result) + xbmcplugin.setProperty( + self.__addon_handle, "FolderName", xbmc.getLocalizedString(KODI_PLAYLISTS_STR_ID) + ) + playlists = self.__prepare_playlist_listitems(result["playlists"]["items"]) + self.__add_playlist_listitems(playlists) + self.__add_next_button(result["playlists"]["total"]) + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + + if self.default_view_playlists: + xbmc.executebuiltin(f"Container.SetViewMode({self.default_view_playlists})") + + def search(self) -> None: + xbmcplugin.setContent(self.__addon_handle, "files") + xbmcplugin.setPluginCategory( + self.__addon_handle, xbmc.getLocalizedString(KODI_SEARCH_RESULTS_STR_ID) + ) + + kb = xbmc.Keyboard("", xbmc.getLocalizedString(KODI_ENTER_SEARCH_STRING_STR_ID)) + kb.doModal() + if kb.isConfirmed(): + value = kb.getText() + items = [] + result = self.__spotipy.search( + q=f"{value}", + type="artist,album,track,playlist", + limit=1, + market=self.__user_country, + ) + items.append( + ( + f"{xbmc.getLocalizedString(KODI_ARTISTS_STR_ID)}" + f" ({result['artists']['total']})", + f"plugin://{ADDON_ID}/" + f"?action={self.search_artists.__name__}&artistid={value}", + ) + ) + items.append( + ( + f"{xbmc.getLocalizedString(KODI_PLAYLISTS_STR_ID)}" + f" ({result['playlists']['total']})", + f"plugin://{ADDON_ID}/" + f"?action={self.search_playlists.__name__}&playlistid={value}", + ) + ) + items.append( + ( + f"{xbmc.getLocalizedString(KODI_ALBUMS_STR_ID)} ({result['albums']['total']})", + f"plugin://{ADDON_ID}/" + f"?action={self.search_albums.__name__}&albumid={value}", + ) + ) + items.append( + ( + f"{xbmc.getLocalizedString(KODI_SONGS_STR_ID)} ({result['tracks']['total']})", + f"plugin://{ADDON_ID}/" + f"?action={self.search_tracks.__name__}&trackid={value}", + ) + ) + for item in items: + li = xbmcgui.ListItem(item[0], path=item[1]) + li.setProperty("do_not_analyze", "true") + li.setProperty("IsPlayable", "false") + li.addContextMenuItems([], True) + xbmcplugin.addDirectoryItem( + handle=self.__addon_handle, url=item[1], listitem=li, isFolder=True + ) + + xbmcplugin.endOfDirectory(handle=self.__addon_handle) + + def __add_next_button(self, list_total: int) -> None: + # Adds a next button if needed. + params = self.__params + if list_total > self.__offset + self.__limit: + params["offset"] = [str(self.__offset + self.__limit)] + url = f"plugin://{ADDON_ID}/" + + for key, value in list(params.items()): + if key == "action": + url += f"?{key}={value[0]}" + elif key == "offset": + url += f"&{key}={value}" + else: + url += f"&{key}={value[0]}" + + li = xbmcgui.ListItem(xbmc.getLocalizedString(KODI_NEXT_PAGE_STR_ID), path=url) + li.setProperty("do_not_analyze", "true") + li.setProperty("IsPlayable", "false") + + xbmcplugin.addDirectoryItem( + handle=self.__addon_handle, url=url, listitem=li, isFolder=True + ) + + def __precache_library(self) -> None: + if not self.__win.getProperty("Spotify.PreCachedItems"): + monitor = xbmc.Monitor() + self.__win.setProperty("Spotify.PreCachedItems", "busy") + user_playlists = self.__get_user_playlists(self.__userid) + for playlist in user_playlists: + self.__get_playlist_details(playlist["id"]) + if monitor.abortRequested(): + return + self.__get_saved_albums() + if monitor.abortRequested(): + return + self.__get_saved_artists() + if monitor.abortRequested(): + return + self.__get_saved_tracks() + del monitor + self.__win.setProperty("Spotify.PreCachedItems", "done") diff --git a/resources/lib/save_recently_played.py b/resources/lib/save_recently_played.py new file mode 100644 index 0000000..7bfdb85 --- /dev/null +++ b/resources/lib/save_recently_played.py @@ -0,0 +1,63 @@ +import xbmc +import xbmcaddon + +import spotipy +import utils +from utils import log_msg, ADDON_ID + +ADDON_SETTING_MY_RECENTLY_PLAYED_PLAYLIST_NAME = "my_recently_played_playlist_name" + + +class SaveRecentlyPlayed: + def __init__(self): + self.__spotipy = None + self.__my_recently_played_playlist_id = None + + def save_track(self, track_id: str) -> None: + my_recently_played_playlist_name = self.__get_my_recently_played_playlist_name() + if not my_recently_played_playlist_name: + return + + if not self.__my_recently_played_playlist_id: + self.__set_my_recently_played_playlist_id() + + self.__spotipy.playlist_add_items(self.__my_recently_played_playlist_id, [track_id]) + log_msg( + f"Saved track '{track_id}' to '{my_recently_played_playlist_name}' playlist.", + xbmc.LOGINFO, + ) + + @staticmethod + def __get_my_recently_played_playlist_name() -> str: + setting = xbmcaddon.Addon(id=ADDON_ID).getSetting( + ADDON_SETTING_MY_RECENTLY_PLAYED_PLAYLIST_NAME + ) + if setting.upper() == "NONE": + setting = "" + return setting + + def __set_my_recently_played_playlist_id(self) -> None: + my_recently_played_playlist_name = self.__get_my_recently_played_playlist_name() + + self.__spotipy = spotipy.Spotify(auth=utils.get_cached_auth_token()) + log_msg(f"Getting id for '{my_recently_played_playlist_name}' playlist.", xbmc.LOGDEBUG) + self.__my_recently_played_playlist_id = utils.get_user_playlist_id( + self.__spotipy, my_recently_played_playlist_name + ) + + if not self.__my_recently_played_playlist_id: + log_msg( + f"Did not find a '{my_recently_played_playlist_name}' playlist." + " Creating one now.", + xbmc.LOGINFO, + ) + userid = self.__spotipy.me()["id"] + playlist = self.__spotipy.user_playlist_create( + userid, my_recently_played_playlist_name, False + ) + self.__my_recently_played_playlist_id = playlist["id"] + + if not self.__my_recently_played_playlist_id: + raise Exception( + f"Could not create a '{my_recently_played_playlist_name}' playlist." + ) diff --git a/resources/lib/string_ids.py b/resources/lib/string_ids.py new file mode 100644 index 0000000..b8dd017 --- /dev/null +++ b/resources/lib/string_ids.py @@ -0,0 +1,39 @@ +KODI_ALBUMS_STR_ID = 132 +KODI_ARTISTS_STR_ID = 133 +KODI_SONGS_STR_ID = 134 +KODI_PLAYLISTS_STR_ID = 136 +KODI_SEARCH_STR_ID = 137 +KODI_PLAY_STR_ID = 208 +KODI_SEARCH_RESULTS_STR_ID = 283 +KODI_SELECT_PLAYLIST_STR_ID = 524 +KODI_ADD_TO_PLAYLIST_STR_ID = 526 +KODI_NEW_PLAYLIST_STR_ID = 525 +KODI_BROWSE_STR_ID = 1024 +KODI_ENTER_NEW_PLAYLIST_STR_ID = 21381 +KODI_NEXT_PAGE_STR_ID = 33078 +KODI_ENTER_SEARCH_STRING_STR_ID = 16017 + +NEW_RELEASES_STR_ID = 11005 +SAVE_TRACKS_TO_MY_MUSIC_STR_ID = 11007 +REMOVE_TRACKS_FROM_MY_MUSIC_STR_ID = 11008 +FOLLOW_PLAYLIST_STR_ID = 11009 +UNFOLLOW_PLAYLIST_STR_ID = 11010 +ARTIST_TOP_TRACKS_STR_ID = 11011 +RELATED_ARTISTS_STR_ID = 11012 +MY_MUSIC_FOLDER_STR_ID = 11013 +EXPLORE_STR_ID = 11014 +FEATURED_PLAYLISTS_STR_ID = 11015 +BROWSE_NEW_RELEASES_STR_ID = 11016 +REMOVE_FROM_PLAYLIST_STR_ID = 11017 +ALL_ALBUMS_FOR_ARTIST_STR_ID = 11018 +MOST_PLAYED_ARTISTS_STR_ID = 11023 +MOST_PLAYED_TRACKS_STR_ID = 11024 +FOLLOW_ARTIST_STR_ID = 11025 +UNFOLLOW_ARTIST_STR_ID = 11026 +REFRESH_LISTING_STR_ID = 11027 +CURRENT_USER_STR_ID = 11047 +NO_CREDENTIALS_MSG_STR_ID = 11050 +HTTP_VIDEO_RULE_ADDED_STR_ID = 11071 +CLEAR_CACHE_STR_ID = 11072 +FOLLOWED_ARTISTS_STR_ID = 11073 +CACHED_CLEARED_STR_ID = 11074 diff --git a/resources/lib/utils.py b/resources/lib/utils.py new file mode 100644 index 0000000..b84494b --- /dev/null +++ b/resources/lib/utils.py @@ -0,0 +1,146 @@ +import inspect +import os +import platform +import signal +import unicodedata +from traceback import format_exception +from typing import Any, Dict, List, Tuple, Union + +import xbmc +import xbmcgui +import xbmcvfs +from xbmc import LOGDEBUG, LOGINFO, LOGERROR + +DEBUG = True +PROXY_PORT = 52308 + +ADDON_ID = "plugin.audio.librespot" +ADDON_DATA_PATH = xbmcvfs.translatePath(f"special://profile/addon_data/{ADDON_ID}") +ADDON_WINDOW_ID = 10000 + +KODI_PROPERTY_SPOTIFY_TOKEN = "spotify-token" + + +def log_msg(msg: str, loglevel: int = LOGDEBUG, caller_name: str = "") -> None: + if DEBUG and (loglevel == LOGDEBUG): + loglevel = LOGINFO + if not caller_name: + caller_name = get_formatted_caller_name(inspect.stack()[1][1], inspect.stack()[1][3]) + + xbmc.log(f"{ADDON_ID}:{caller_name}: {msg}", level=loglevel) + + +def log_exception(exc: Exception, exception_details: str) -> None: + the_caller_name = get_formatted_caller_name(inspect.stack()[1][1], inspect.stack()[1][3]) + log_msg(" ".join(format_exception(exc)), loglevel=LOGERROR, caller_name=the_caller_name) + log_msg(f"Exception --> {exception_details}.", loglevel=LOGERROR, caller_name=the_caller_name) + + +def get_formatted_caller_name(filename: str, function_name: str) -> str: + return f"{os.path.splitext(os.path.basename(filename))[0]}:{function_name}" + + +def kill_process_by_pid(pid: int) -> None: + try: + if platform.system() != "Windows": + os.kill(pid, signal.SIGKILL) + except OSError: + pass + + +def bytes_to_megabytes(byts: int) -> float: + return (byts / 1024.0) / 1024.0 + + +def get_chunks(data, chunk_size: int): + return [data[x : x + chunk_size] for x in range(0, len(data), chunk_size)] + + +def try_encode(text, encoding="utf-8"): + try: + return text.encode(encoding, "ignore") + except UnicodeEncodeError: + return text + + +def try_decode(text, encoding="utf-8"): + try: + return text.decode(encoding, "ignore") + except UnicodeDecodeError: + return text + + +def normalize_string(text): + text = text.replace(":", "") + text = text.replace("/", "-") + text = text.replace("\\", "-") + text = text.replace("<", "") + text = text.replace(">", "") + text = text.replace("*", "") + text = text.replace("?", "") + text = text.replace("|", "") + text = text.replace("(", "") + text = text.replace(")", "") + text = text.replace('"', "") + text = text.strip() + text = text.rstrip(".") + text = unicodedata.normalize("NFKD", try_decode(text)) + + return text + + +def cache_auth_token(auth_token: str) -> None: + cache_value_in_kodi(KODI_PROPERTY_SPOTIFY_TOKEN, auth_token) + + +def get_cached_auth_token() -> str: + return get_cached_value_from_kodi(KODI_PROPERTY_SPOTIFY_TOKEN) + + +def cache_value_in_kodi(kodi_property_id: str, value: Any): + win = xbmcgui.Window(ADDON_WINDOW_ID) + win.setProperty(kodi_property_id, value) + + +def get_cached_value_from_kodi(kodi_property_id: str, wait_ms: int = 500) -> Any: + win = xbmcgui.Window(ADDON_WINDOW_ID) + + count = 10 + while count > 0: + value = win.getProperty(kodi_property_id) + if value: + return value + xbmc.sleep(wait_ms) + count -= 1 + + return None + + +def get_user_playlists( + spotipy, limit: int = 50, offset: int = 0 +) -> Tuple[List[Dict[str, Any]], List[str]]: + userid = spotipy.me()["id"] + playlists = spotipy.user_playlists(userid, limit=limit, offset=offset) + + own_playlists = [] + own_playlist_names = [] + for playlist in playlists["items"]: + if playlist["owner"]["id"] == userid: + own_playlists.append(playlist) + own_playlist_names.append(playlist["name"]) + + return own_playlists, own_playlist_names + + +def get_user_playlist_id(spotipy, playlist_name: str) -> Union[str, None]: + offset = 0 + while True: + own_playlists, own_playlist_names = get_user_playlists(spotipy, limit=50, offset=offset) + if len(own_playlists) == 0: + break + for playlist in own_playlists: + if playlist_name == playlist["name"]: + return playlist["id"] + offset += 50 + + return None diff --git a/resources/settings.xml b/resources/settings.xml new file mode 100644 index 0000000..8310211 --- /dev/null +++ b/resources/settings.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + +