From ae28da8d60cc09bcdbd487101661845084726b48 Mon Sep 17 00:00:00 2001 From: cere Date: Wed, 21 Feb 2024 00:35:31 -0500 Subject: [PATCH] code time --- .gitignore | 6 +- lib/protobuf/__init__.py | 33 + lib/protobuf/any_pb2.py | 26 + lib/protobuf/api_pb2.py | 32 + lib/protobuf/compiler/__init__.py | 0 lib/protobuf/compiler/plugin_pb2.py | 35 + lib/protobuf/descriptor.py | 1224 +++++ lib/protobuf/descriptor_database.py | 177 + lib/protobuf/descriptor_pb2.py | 1925 +++++++ lib/protobuf/descriptor_pool.py | 1295 +++++ lib/protobuf/duration_pb2.py | 26 + lib/protobuf/empty_pb2.py | 26 + lib/protobuf/field_mask_pb2.py | 26 + lib/protobuf/internal/__init__.py | 0 ...ementation.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 6256 bytes lib/protobuf/internal/api_implementation.py | 112 + lib/protobuf/internal/builder.py | 130 + lib/protobuf/internal/containers.py | 710 +++ lib/protobuf/internal/decoder.py | 1029 ++++ lib/protobuf/internal/encoder.py | 829 ++++ lib/protobuf/internal/enum_type_wrapper.py | 124 + lib/protobuf/internal/extension_dict.py | 213 + lib/protobuf/internal/message_listener.py | 78 + lib/protobuf/internal/python_message.py | 1539 ++++++ lib/protobuf/internal/type_checkers.py | 435 ++ lib/protobuf/internal/well_known_types.py | 878 ++++ lib/protobuf/internal/wire_format.py | 268 + lib/protobuf/json_format.py | 912 ++++ lib/protobuf/message.py | 424 ++ lib/protobuf/message_factory.py | 185 + lib/protobuf/proto_builder.py | 134 + lib/protobuf/pyext/__init__.py | 0 .../_message.cpython-310-x86_64-linux-gnu.so | Bin 0 -> 2524904 bytes lib/protobuf/pyext/cpp_message.py | 65 + lib/protobuf/reflection.py | 95 + lib/protobuf/service.py | 228 + lib/protobuf/service_reflection.py | 295 ++ lib/protobuf/source_context_pb2.py | 26 + lib/protobuf/struct_pb2.py | 36 + lib/protobuf/symbol_database.py | 194 + lib/protobuf/text_encoding.py | 110 + lib/protobuf/text_format.py | 1795 +++++++ lib/protobuf/timestamp_pb2.py | 26 + lib/protobuf/type_pb2.py | 42 + lib/protobuf/util/__init__.py | 0 lib/protobuf/util/json_format_pb2.py | 72 + lib/protobuf/util/json_format_proto3_pb2.py | 129 + lib/protobuf/wrappers_pb2.py | 42 + resources/fanart.jpg | Bin 0 -> 156453 bytes resources/icon.png | Bin 0 -> 17021 bytes resources/icon_clear_cache.png | Bin 0 -> 2476 bytes resources/icon_music_albums.png | Bin 0 -> 2216 bytes resources/icon_music_artists.png | Bin 0 -> 2958 bytes resources/icon_music_explore.png | Bin 0 -> 4769 bytes resources/icon_music_library.png | Bin 0 -> 3159 bytes resources/icon_music_playlists.png | Bin 0 -> 964 bytes resources/icon_music_search.png | Bin 0 -> 1981 bytes resources/icon_music_songs.png | Bin 0 -> 1527 bytes resources/icon_music_top_artists.png | Bin 0 -> 1404 bytes resources/icon_music_top_tracks.png | Bin 0 -> 301 bytes .../resource.language.de_de/strings.po | 179 + .../resource.language.en_gb/strings.po | 175 + .../resource.language.es_ar/strings.po | 175 + .../resource.language.es_es/strings.po | 175 + .../resource.language.es_mx/strings.po | 175 + .../resource.language.fr_fr/strings.po | 179 + .../resource.language.he_he/strings.po | 182 + .../resource.language.nl_nl/strings.mo | Bin 0 -> 5879 bytes .../resource.language.nl_nl/strings.po | 179 + resources/lib/__init__.py | 50 + resources/lib/bottle_manager.py | 61 + resources/lib/defusedxml/ElementTree.py | 154 + resources/lib/defusedxml/__init__.py | 67 + resources/lib/defusedxml/cElementTree.py | 62 + resources/lib/defusedxml/common.py | 129 + resources/lib/defusedxml/expatbuilder.py | 107 + resources/lib/defusedxml/expatreader.py | 61 + resources/lib/defusedxml/lxml.py | 153 + resources/lib/defusedxml/minidom.py | 63 + resources/lib/defusedxml/pulldom.py | 41 + resources/lib/defusedxml/sax.py | 60 + resources/lib/defusedxml/xmlrpc.py | 153 + resources/lib/deps/__init__.py | 4 + resources/lib/deps/bottle.py | 4417 +++++++++++++++++ resources/lib/deps/simplecache.py | 307 ++ resources/lib/deps/spotipy/__init__.py | 5 + resources/lib/deps/spotipy/cache_handler.py | 173 + resources/lib/deps/spotipy/client.py | 2035 ++++++++ resources/lib/deps/spotipy/exceptions.py | 16 + resources/lib/deps/spotipy/oauth2.py | 1308 +++++ resources/lib/deps/spotipy/util.py | 135 + resources/lib/http_video_player_setter.py | 84 + resources/lib/librespot/__init__.py | 34 + resources/lib/librespot/audio/__init__.py | 912 ++++ resources/lib/librespot/audio/decoders.py | 81 + resources/lib/librespot/audio/decrypt.py | 45 + resources/lib/librespot/audio/format.py | 32 + resources/lib/librespot/audio/storage.py | 139 + resources/lib/librespot/cache.py | 18 + resources/lib/librespot/core.py | 2307 +++++++++ resources/lib/librespot/crypto.py | 412 ++ resources/lib/librespot/dealer.py | 11 + resources/lib/librespot/mercury.py | 394 ++ resources/lib/librespot/metadata.py | 283 ++ .../lib/librespot/proto/Authentication_pb2.py | 1984 ++++++++ .../lib/librespot/proto/CanvazMeta_pb2.py | 91 + resources/lib/librespot/proto/Canvaz_pb2.py | 564 +++ .../lib/librespot/proto/ClientToken_pb2.py | 63 + resources/lib/librespot/proto/Connect_pb2.py | 2340 +++++++++ .../lib/librespot/proto/Connectivity_pb2.py | 36 + .../lib/librespot/proto/ContextPage_pb2.py | 239 + .../proto/ContextPlayerOptions_pb2.py | 215 + .../lib/librespot/proto/ContextTrack_pb2.py | 214 + resources/lib/librespot/proto/Context_pb2.py | 260 + .../proto/ExplicitContentPubsub_pb2.py | 147 + .../lib/librespot/proto/Keyexchange_pb2.py | 2497 ++++++++++ resources/lib/librespot/proto/Mercury_pb2.py | 625 +++ resources/lib/librespot/proto/Metadata_pb2.py | 3698 ++++++++++++++ .../lib/librespot/proto/PlayOrigin_pb2.py | 193 + resources/lib/librespot/proto/Playback_pb2.py | 162 + resources/lib/librespot/proto/Player_pb2.py | 1851 +++++++ .../librespot/proto/Playlist4External_pb2.py | 3221 ++++++++++++ .../librespot/proto/PlaylistAnnotate3_pb2.py | 460 ++ resources/lib/librespot/proto/Pubsub_pb2.py | 112 + resources/lib/librespot/proto/Queue_pb2.py | 104 + .../lib/librespot/proto/Restrictions_pb2.py | 480 ++ resources/lib/librespot/proto/Session_pb2.py | 149 + .../lib/librespot/proto/StorageResolve_pb2.py | 154 + .../lib/librespot/proto/TransferState_pb2.py | 174 + resources/lib/librespot/proto/__init__.py | 0 .../lib/librespot/proto/spotify/__init__.py | 0 .../proto/spotify/login5/__init__.py | 0 .../proto/spotify/login5/v3/ClientInfo_pb2.py | 98 + .../proto/spotify/login5/v3/Login5_pb2.py | 972 ++++ .../proto/spotify/login5/v3/UserInfo_pb2.py | 263 + .../proto/spotify/login5/v3/__init__.py | 0 .../spotify/login5/v3/challenges/Code_pb2.py | 224 + .../login5/v3/challenges/Hashcash_pb2.py | 176 + .../spotify/login5/v3/challenges/__init__.py | 0 .../login5/v3/credentials/Credentials_pb2.py | 483 ++ .../spotify/login5/v3/credentials/__init__.py | 0 .../login5/v3/identifiers/Identifiers.py | 119 + .../spotify/login5/v3/identifiers/__init__.py | 0 resources/lib/librespot/structure.py | 103 + resources/lib/librespot/util.py | 117 + resources/lib/librespot/zeroconf.py | 345 ++ resources/lib/librespot_auth.py | 66 + resources/lib/main_service.py | 151 + resources/lib/plugin_content.py | 1637 ++++++ resources/lib/save_recently_played.py | 63 + resources/lib/string_ids.py | 39 + resources/lib/utils.py | 146 + resources/settings.xml | 16 + 153 files changed, 56768 insertions(+), 1 deletion(-) create mode 100644 lib/protobuf/__init__.py create mode 100644 lib/protobuf/any_pb2.py create mode 100644 lib/protobuf/api_pb2.py create mode 100644 lib/protobuf/compiler/__init__.py create mode 100644 lib/protobuf/compiler/plugin_pb2.py create mode 100644 lib/protobuf/descriptor.py create mode 100644 lib/protobuf/descriptor_database.py create mode 100644 lib/protobuf/descriptor_pb2.py create mode 100644 lib/protobuf/descriptor_pool.py create mode 100644 lib/protobuf/duration_pb2.py create mode 100644 lib/protobuf/empty_pb2.py create mode 100644 lib/protobuf/field_mask_pb2.py create mode 100644 lib/protobuf/internal/__init__.py create mode 100755 lib/protobuf/internal/_api_implementation.cpython-310-x86_64-linux-gnu.so create mode 100644 lib/protobuf/internal/api_implementation.py create mode 100644 lib/protobuf/internal/builder.py create mode 100644 lib/protobuf/internal/containers.py create mode 100644 lib/protobuf/internal/decoder.py create mode 100644 lib/protobuf/internal/encoder.py create mode 100644 lib/protobuf/internal/enum_type_wrapper.py create mode 100644 lib/protobuf/internal/extension_dict.py create mode 100644 lib/protobuf/internal/message_listener.py create mode 100644 lib/protobuf/internal/python_message.py create mode 100644 lib/protobuf/internal/type_checkers.py create mode 100644 lib/protobuf/internal/well_known_types.py create mode 100644 lib/protobuf/internal/wire_format.py create mode 100644 lib/protobuf/json_format.py create mode 100644 lib/protobuf/message.py create mode 100644 lib/protobuf/message_factory.py create mode 100644 lib/protobuf/proto_builder.py create mode 100644 lib/protobuf/pyext/__init__.py create mode 100755 lib/protobuf/pyext/_message.cpython-310-x86_64-linux-gnu.so create mode 100644 lib/protobuf/pyext/cpp_message.py create mode 100644 lib/protobuf/reflection.py create mode 100644 lib/protobuf/service.py create mode 100644 lib/protobuf/service_reflection.py create mode 100644 lib/protobuf/source_context_pb2.py create mode 100644 lib/protobuf/struct_pb2.py create mode 100644 lib/protobuf/symbol_database.py create mode 100644 lib/protobuf/text_encoding.py create mode 100644 lib/protobuf/text_format.py create mode 100644 lib/protobuf/timestamp_pb2.py create mode 100644 lib/protobuf/type_pb2.py create mode 100644 lib/protobuf/util/__init__.py create mode 100644 lib/protobuf/util/json_format_pb2.py create mode 100644 lib/protobuf/util/json_format_proto3_pb2.py create mode 100644 lib/protobuf/wrappers_pb2.py create mode 100644 resources/fanart.jpg create mode 100644 resources/icon.png create mode 100644 resources/icon_clear_cache.png create mode 100644 resources/icon_music_albums.png create mode 100644 resources/icon_music_artists.png create mode 100644 resources/icon_music_explore.png create mode 100644 resources/icon_music_library.png create mode 100644 resources/icon_music_playlists.png create mode 100644 resources/icon_music_search.png create mode 100644 resources/icon_music_songs.png create mode 100644 resources/icon_music_top_artists.png create mode 100644 resources/icon_music_top_tracks.png create mode 100644 resources/language/resource.language.de_de/strings.po create mode 100644 resources/language/resource.language.en_gb/strings.po create mode 100644 resources/language/resource.language.es_ar/strings.po create mode 100644 resources/language/resource.language.es_es/strings.po create mode 100644 resources/language/resource.language.es_mx/strings.po create mode 100644 resources/language/resource.language.fr_fr/strings.po create mode 100644 resources/language/resource.language.he_he/strings.po create mode 100644 resources/language/resource.language.nl_nl/strings.mo create mode 100644 resources/language/resource.language.nl_nl/strings.po create mode 100644 resources/lib/__init__.py create mode 100644 resources/lib/bottle_manager.py create mode 100644 resources/lib/defusedxml/ElementTree.py create mode 100644 resources/lib/defusedxml/__init__.py create mode 100644 resources/lib/defusedxml/cElementTree.py create mode 100644 resources/lib/defusedxml/common.py create mode 100644 resources/lib/defusedxml/expatbuilder.py create mode 100644 resources/lib/defusedxml/expatreader.py create mode 100644 resources/lib/defusedxml/lxml.py create mode 100644 resources/lib/defusedxml/minidom.py create mode 100644 resources/lib/defusedxml/pulldom.py create mode 100644 resources/lib/defusedxml/sax.py create mode 100644 resources/lib/defusedxml/xmlrpc.py create mode 100644 resources/lib/deps/__init__.py create mode 100755 resources/lib/deps/bottle.py create mode 100644 resources/lib/deps/simplecache.py create mode 100644 resources/lib/deps/spotipy/__init__.py create mode 100644 resources/lib/deps/spotipy/cache_handler.py create mode 100644 resources/lib/deps/spotipy/client.py create mode 100644 resources/lib/deps/spotipy/exceptions.py create mode 100644 resources/lib/deps/spotipy/oauth2.py create mode 100644 resources/lib/deps/spotipy/util.py create mode 100644 resources/lib/http_video_player_setter.py create mode 100644 resources/lib/librespot/__init__.py create mode 100644 resources/lib/librespot/audio/__init__.py create mode 100644 resources/lib/librespot/audio/decoders.py create mode 100644 resources/lib/librespot/audio/decrypt.py create mode 100644 resources/lib/librespot/audio/format.py create mode 100644 resources/lib/librespot/audio/storage.py create mode 100644 resources/lib/librespot/cache.py create mode 100644 resources/lib/librespot/core.py create mode 100644 resources/lib/librespot/crypto.py create mode 100644 resources/lib/librespot/dealer.py create mode 100644 resources/lib/librespot/mercury.py create mode 100644 resources/lib/librespot/metadata.py create mode 100644 resources/lib/librespot/proto/Authentication_pb2.py create mode 100644 resources/lib/librespot/proto/CanvazMeta_pb2.py create mode 100644 resources/lib/librespot/proto/Canvaz_pb2.py create mode 100644 resources/lib/librespot/proto/ClientToken_pb2.py create mode 100644 resources/lib/librespot/proto/Connect_pb2.py create mode 100644 resources/lib/librespot/proto/Connectivity_pb2.py create mode 100644 resources/lib/librespot/proto/ContextPage_pb2.py create mode 100644 resources/lib/librespot/proto/ContextPlayerOptions_pb2.py create mode 100644 resources/lib/librespot/proto/ContextTrack_pb2.py create mode 100644 resources/lib/librespot/proto/Context_pb2.py create mode 100644 resources/lib/librespot/proto/ExplicitContentPubsub_pb2.py create mode 100644 resources/lib/librespot/proto/Keyexchange_pb2.py create mode 100644 resources/lib/librespot/proto/Mercury_pb2.py create mode 100644 resources/lib/librespot/proto/Metadata_pb2.py create mode 100644 resources/lib/librespot/proto/PlayOrigin_pb2.py create mode 100644 resources/lib/librespot/proto/Playback_pb2.py create mode 100644 resources/lib/librespot/proto/Player_pb2.py create mode 100644 resources/lib/librespot/proto/Playlist4External_pb2.py create mode 100644 resources/lib/librespot/proto/PlaylistAnnotate3_pb2.py create mode 100644 resources/lib/librespot/proto/Pubsub_pb2.py create mode 100644 resources/lib/librespot/proto/Queue_pb2.py create mode 100644 resources/lib/librespot/proto/Restrictions_pb2.py create mode 100644 resources/lib/librespot/proto/Session_pb2.py create mode 100644 resources/lib/librespot/proto/StorageResolve_pb2.py create mode 100644 resources/lib/librespot/proto/TransferState_pb2.py create mode 100644 resources/lib/librespot/proto/__init__.py create mode 100644 resources/lib/librespot/proto/spotify/__init__.py create mode 100644 resources/lib/librespot/proto/spotify/login5/__init__.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/ClientInfo_pb2.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/Login5_pb2.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/UserInfo_pb2.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/__init__.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/challenges/Code_pb2.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/challenges/Hashcash_pb2.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/challenges/__init__.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/credentials/Credentials_pb2.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/credentials/__init__.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/identifiers/Identifiers.py create mode 100644 resources/lib/librespot/proto/spotify/login5/v3/identifiers/__init__.py create mode 100644 resources/lib/librespot/structure.py create mode 100644 resources/lib/librespot/util.py create mode 100644 resources/lib/librespot/zeroconf.py create mode 100644 resources/lib/librespot_auth.py create mode 100644 resources/lib/main_service.py create mode 100644 resources/lib/plugin_content.py create mode 100644 resources/lib/save_recently_played.py create mode 100644 resources/lib/string_ids.py create mode 100644 resources/lib/utils.py create mode 100644 resources/settings.xml 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 0000000000000000000000000000000000000000..ba004e29e48e2e6d478702e702f5b1b153f279fc GIT binary patch literal 6256 zcmeHLZ)_Y#6`ynL=6p%d*D6iYq-sVel^lW>``k21OQ_S!pLLZqhjA(@D4X@|+PCO# z_qd&-wyA`LsZ@h38ITY{;tLX};#1&L>4)Otszen?DOD@0Al3Y-IF||xQmPbzkN0NZ zvo{-`D)E62D08}f^Zxwadow%pc6PsV`rMh0R7!B$CO#>UtKAk!hDfn?2X%l9iDP0r zT=$8)wQbj?>ZacH?nuN`2&n}a14As{u34Z`cbGOiBKaCo#vzNi6~e6$j;UaKqUp$u z=md)YD>T$3JxF#;(H2|r9pzyEb2N@5WqYQK`xM}43bmgPyX44VgyOZEmmwM#QyPvQ z+b;024gO~zyC{0U@T;%Cy>R@Ev)}&nd~UJ(==a}x@D~^Y^*ARy|IaSURA=YV9Lh5A z$7y}xkKcN_aQcn+UQFNn{_+<;bI;G_|DOHJ>wn9Fs%=AmatrM|$arjV^*zAIxPbb{ z+Q>&h{*f*0e+aUz{6F1B-UW%~#2#@uCvWif4{g6&JeQE$Aiq=W5SO+`<`)U|fwtds zlRf5gr|1^B#LeHWaMNa{HEZf;|%+%5crUm59t)AtW&OCsRSia@y0^slnx&@!oYX{+3H4bqExa%%*Mu(L z`^G*Gc4&vl!2QL>b%ghSk>>ph)eDw$ox7Tl=g59JA!oiu>xBL2O$BjRdL)L|HHuQv zd^0A;J;-e>Cg<9Ly6Z7{yuR+GxR@T50_{r1k~C-EOE)LLr8)a@ zdP#`Zkw0`x(Yo(XaP2uT1U=--7;|-{1?|4KkjJ5}Ue)|>kjKP=1c3_%(*r5rRKwC>J{^~b+ro|TuXxz;-y ze&)nC0Vf(Czi7^$coVE3)pyiRaQoxiq-fp_Lui)a@=BKncBv;hF4NU7gL2&9^cOz; z;x!mMz+VSn&A+b03_#eUFW>}0l20O;`=vRz)cgVHo>^>U&AH{zE^Ur~_He3^Hs{{f z`hnkSZO;L?Juq+4Edm$q8*ckAx%CkD`&Knz3z_snY0IiE4N{eDCEdwd5W1l(1=X5Y zar=~4b%BYhL#S;sjteEK>iI6Pu5{dSyHQc{WN}#DSaqZdWFvItcu<$cDOC=9S*r&s z$Qzl=u+kx-8U;z*RI-fPlpLFqp{tbVPjHkF93mS}FSD5*d;3 z3wf(hELy|oi|4Gz^M?$$C8_<}9+E(D@xT19R_hZ$cm2E7!j;_#ciTZ8{@7B}BO*1K zOYPseJ-q-M=A?b-2EdPY3F@1c(l9@LLRw1MR-z6*Z!v*~0u zH`~$3WYdrC7|o`2eZ)A=f}b9sx7w{4xHSX+KQqAR8a~G)&no=JKv4-`QLK5zi?0RK5F+Jh5geqXI9(`z4u|wGoJT3 zQ#>PK<9*BfEs_e+3mwdX@mvR(7HL6ZTZsATs(Ah+7NgL?u`!Rm? zdMSp#3;IcX8Dy9jmjQkbFwGL4N%H=e5_n0DnCi1GgMsq2xB#WJg55n!vaFp2)Cd{DlO5h7x8v$v~of|5*Gz z=-@r#^})|+{@$3JFUB+d5n%B>g7N$uze)!IwgVI0m}mM^km3|Ep1-Foe+WMECdAL7 zgG+|-{QTnY!Mrhe-Z(Cg>k44w@%ekr3JwaI)Yr`m%RB=3?D|n$|eTX;4F?|!1 zar_XSONJ=HavThOnc#m1Jo@7C^Y_Z<2+wgC#`D1Z@1cY7`&mGg@lPb=Xp1dQId-Ip QuBkgB`fgkZea5l)Kl7c$g8%>k literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..6e81155f8fa95da930bd5c77d48cf43a95552ea9 GIT binary patch literal 2524904 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%0DjR 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%0AOwxzy|= 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 z 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

?>F%Bl-W0cwak~!C^3p?{-LYHu4 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

0R3S!B|`YDwqBWQ zYipfZ%PndlcfD0B|9RG0P5V8*C&NXbYl<(sQSU}$IpjkL0~W+#NX#UMFsvWa%em8_ z)mGx0((zNqi}O`{UQpk?SiTw>&Mw+|(<$DY8JLS439C1CpJC0p7J}*?bPIGGj8_xZ zowgz@ME^T_fMK&cZ1GYr`O;hHru`*+5MBs3IH2|Ecu{=z75-7A!NRvLmy-J3C_OL) zLd0?i+Twg2&QiF}`Cs)6xXynP6EoUY$N|rQtNm8qY<%}dL&{Rf{WEGdGH2H5{RVLW z;8UsBfBS4=-#Vjj$Vad3jlN5I#nww>A}A=%^Zgt(9RAzwxX;@pJG>mOvnV#4`Jm)j ztmBc~XDcbr@V&>W=n=!$t^N3DaZV4X>nao1W#dy)0n+NKuYee?x0~0AxH8`yZJ;>Abc|vUcUVXqoc36CMevkgUS&6;xx}1DU z{l73W*O^mMSaQ^lFt$4V!+S`vGr}_j#yg~cu#n7HC7AJg$&7eRX4DuZiR?cw@bev! z+-IctVxS;_)?66#CbKgx5RnwQ>e`_;JAJn%=`I;;6G`?{{(ar zsq9+9=U|raLQinrimPZ(%B%C1ygDVuGyEh%M0+D^gD?9SK3{@QS71;7FCl{3IlR@Y z8rT?46D^pIXQU&t^rnkYFa2?9rwrmC1ExcM4UZ&e7Dny>z-#({!oV=7-LvtTQLPsp@lx(plY)kpi&2L5PD35FZ(d84@rd)&knSPB-xb$ z6tdm>`&#RRiubhv2_RhC0vOf9{Jd50aHrA$pT}vol|# z=d`*SzIxBB$(*zZk(^n$0ICEi0jL(Hp;GqexcG(|rKCt1N@B9M;uE+pr(i$^*ePuE zp~b*@*)GG%yc3fISj19uv|-|g;V6Egax@zvCu3n3`boUMi}a6A4>ErG;g=@rcl1VA zw9PoQIv#XX;x`IPl02aZdH~(3KlDqLXXdNOVDk?R&*GHO%If+b+tnF+&8_~r2~g@+ zP4YVMs%KoS-Onf60f^SQLFlQAb)&p!LBI>dlS9NcOLzB&*LB?;Hdnh$_kwlMm&z58N>VZ#pz z1f$rKgvGWu;(_q_$NBIK*?cEfpGb|kc+-Ku$ByQQ-1|>+OT7(d zQ3A%GTT1iHqP=to#Lf?1CR>xkK(5=7tATa!1-R}EPiMS=&cIr|fW0K+;?hDZ;i`U}2SFd%(m8ic@GZY3nA%>_1BMQ)Xu8COp` z&+HdfJ)kUAPh<#LW%(A`Ir{S_@C*;k7(U^NYZeHinrKoMCx$06`e0? ze#Rd2A|w&6Ck})7Py_oI;@PQ~n>f-2TCW)D(D~$nFqVPJlV3uls_h7g<)U$+6VlOP z*bL;-VZwYoRn|`X(}zW>rnAD+F}pqPh#`&R21!S`q3j4Tc9;TpjVL=Djmk`{Z(xQ% z1@QpZ1zv<$8DM2@e1KO9Q;V`Iax1KlCH}(D4cR@Dr__qG&nA0GSB{yEX9Mi_Ec6v; z=g&yTfE8i{dWOE^{Er=Wek(c~ zg+B~!YQkumUqr|A#Q5?2kz+37xlQx?$u?^eZHW&{#mpR{gPb;1UC7C&5&FNv9v6cT z^Yvi@Apmm9lC8QRv;kBU!e7YI;_1x=VQjqty&VWXrOpum@0ZRK!gLpehsvC-<~-AU z#}jG>5ZIg+nx9~xJTvbkqSip>0)g1ATmZ;gK7^kqU*D|ocu2uPr;%nMT+edGLbz1TR(J$a%2`(r*n9(0Q}

wbAFfzKu4fI%pJa(wF4 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

#b` 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

_|ocWn~G={mz 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 z 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

8J>VO)p0rFo(FS8F-r@S6gUU7?8@WeBE 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�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 MA1)OMs$%Vb0VkegT>t<8 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..d05ecb000c1a825795e45a335af72c0b934a1500 GIT binary patch literal 156453 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

(LE&J(6ktJ+eAV*MjumU{9@U@l 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

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 @@ + + + + + + + + + + + + + + + +

sPxO}eu%a||sup<>r651Aodm-~+700r zhX}WgGHW5|Y^07xn~DJ(<9ebRnd>!m##E>*_=v_gPZz9#V}y+5x5iryGuC~UPDs~# zMQcsnWkSoa8gi{F2d#7+A}(xXH)1qc?OqV5?$Ms8Dnt3m$Lx6rce?H71AE?AHBus) zX#P5G9SiAjC|B;&lwqWj$?{LJGKK4^-gckAHjU6fu9i|pEBod;Z3yeBs0A6|Zm-b^ zjoze4`()C!qbXjKmnaqr?*gHY#4BqKuRWPLAx>gNy$q03*VjZV0JCMcyoZ*pROrOq zkoj>XSUQ3IqRb=u>OB8tJI_YQK}x|Mg8 z;Myq7gS4TRJ8UYUQq~kdn?i3UG(;DMDfw7&xhyH*gGFisw>6<%Jo*#}E9aEf$NN8fyw+80C@+!nt@g`4$E@!u6pg@-7pH+?&2Yv8=;>CtK`2FrB`$w9P|ooForgu8rei%4d;Pe=L_N8qY0ipG#!;j z!r>eQUKUV|)fYdw@SHDqTkebr{JDmwPThzFJ0e>tmRQvYU2qKO>o0kasLx^2_Wwj<7PRBb`D&O^%V%40f zNfoZdo#lP)le~H%$Y%C+X-mOayeM2?Fk(ucef#v~R$H>m2Q_6%hNmV|CxA-&L>o1+ zq?3TO9pO6w6961UAXKICzQ`lT3uDI@GPRa(8MJYkJ|Gc=PQkWI;Q}2`1WLaWWn?J}sev~&eH#MMGy`rOf zo=5oUNMR>%j?Nb6-7JNNlf8vX&tN3ofL~+<9|dMb4=7@kV5Z`ru?_TA$@65esssP3 zAe_wo^?=J4c=`&BPfHHI*R8%Gr0RQ;2<=<>QdnP?8KNkpVL2#OMBB=u$! z%qOJ7POqzd2ScnQ`h_xurkX_RqGTVAEM$bns&dy3S|^ve@fy|G*H7A)(=7p^wUO=+ zVm6B)f4e-g<6LsHRI!(d4+NJKggIEj3#h>Z@enm&_bI05gjZ?3T|_fQRZUP1f$xSr zg-eB40xcIsgEvfZRnw+@=R=*mJ;GRXsOTD5Elo4j=F^ooqFE)6Chyk=(y$6zX&duF zUcJ)PduBW-ve`v7%N3~Wy+Y0}t>QkXSf2aUlV6QcIP(QoBl3V$(114!Nt2Z;M*>g1I+VfO}pQatG9vx8s40Y8ddenMS!(^I=OR6IgX22m<4lhDF? z%`9z>qo2&*l!rq|pI_W{N^aZUL+pv(Ys80UPx2ey^8MP$d3R{6{TOs991zLvwcd>< z+@bAaR!ja^iHB*=>+@BWDI9gNrRZasa;#@^inHj`nhQo@9*+)lIXLFA4|Gw6PA%DViebGCuiSTF2mlu2=J+Zc#jC z#qjN8o~iazdF|otrPtUccgvTdZ`su0Wcj{Q$|dSn&E;eIBR(#WbO-)Uo<^BzQMv(B z#LFCEJo|?rM1l8$#RuIm?}HLMy)p%`5d>if+iep}ZWVLknjCqnU6$arNp$B*3Qp=P zk_c*|Fe^<`P?r#5Nyrv)oJ?~@>R5teGLt~o#nU!vgwAYt-YCm<;qqN=w1m4~{I^_w zgn=GvDkjy+kf_DsJ=Q)XyF2`_mhT;r0Xw>CVsa%CiKd#+N$g z);vBIQ&t%C>WU|B;+666h^ot5qVztb5aJiD^iu0wll38b4T7%<5dMIT6ewvV5cKk>+kD*f(!5Di|BUP?PL$`{%Lo)t(6QY zeH0<5v6WS>0s>Roe6wxpT^@uibnrQ5D_E6Tziw=qh<3lvmsz1_@?EDZ1yo#jC`!rp zNzdyQQzJm-=;?a&^~g^!d+$abs3*!{($xm(Cl~QzKuW{-Xn)@IMd8BKc zX^%9U!uDi`!DLBCH7uozpA|_m$+1QomPSA0k?7{{t}#m!;-*#Xcc*~|Yr!@WC3NX5 z%cNUJE1wX|k}uO8qDhS{b*qvmjYRAf@(qV;A-EfziY4!^FFe$>xbh~XTW%~#9)Y%A ztiT7jPpBZUOmn^l1pD$*aHteAEO>{X>nOt(YRfWIo8|Pe&if;Iq_eV;T(=2&R%J@> z2rnF+*4GW>R5F9jz@|LvkgR+cmu?jOs)!di=Jt=`Yh+`isn#$_n=Rp4%NJ{X<8Iy7 zv#V}}ILor5e=PQ*f-!HyY-=#?Kr)YVs+0S2s?}pQLRgYOaC?%2}3q`;B$TaLaH|VLPn(MYb>p8md0dtMKFTAP| zGTKHd`iL7fFHqW*$H!G0x*8*W%$j^gVN{cbUh7+{Da9xdM6GoFAF@D<7%I37p+5+MTG@#|3**#{g5EC=8^GEOFi1Y$l^m% zb>B}aer0U=g~P3aPucWqF-lujvk9YqMLX=Rn@ji1yFlqk!x6#8Srzb|>M$pe$xz)h zH2b>UhC>maPLGjFPEr}qhaCdDyn5`<(!(_dwi4V<5t;IzEyy22b=qtUjH*0xffur* zOX`^E7tB*B&9R|3A<^kpI?_!Ij9y<5hR;wfIuebRLO?$0(Vs$X-N$UG)|C+T>v1yx z)B>=WQJicbP#g6)-Z-TJD+?-fwUXp|n>*{h^un`EI7+@pEZ@<{J)B87`n3pS>+>4^ zi3gNdZ#l5X5aN>q8^Cq4N`{EpXL|dO!4KE^Vs@>AXJQ&$=k*L(#d;|PELiXMrToAx zy%7JQ_)D!_6GO~3stP9VI}E3E_xCi-em^~;lJ*EV*% z*?RAa2mJvOFB7r7*u|UY?Z)#YP$@$k7o=O!Phk*_=^aLJR0w~cFLBxnk&DR+B zovDA7mC{iHY5#&@?dKQ2Np2pmyb0OEBTAn>vnD@QVuHe4X^Z-EgV-p+-s4jTl1~rU0A+zO7(E*e6=Fk z_Uw2I@)GqhJL6rv=ZlqpV3RV_-z1Cn0WJ2Lixc9DMEB9CyMj1dP0i|NLKd>8qG_tT^9Yu&+6R->9??#LdSoE)(4< zT*bdFf)0%vzZ;9rjg$CYy+_=l^68z7`%UuV%<+HOZZ~aRz(WpqAQxxfPwv)S;6)3K zHBZMo7IvCi-UQ5<_S_#&6uS2dpH^6bLB#wfXreTV*i)$;r#b9-}Az|Nu@F2MN>U! z%@;HFkkSdQQva~_K-EgqI>EFxA;+dnNJmV>)sDTb7~g+bqxbk5@1rg*On%IqD^pq#z@WVnUu1*FUha zsY*xtdCASFwr6`mXS<@rE+SqKKvG5Cwh>J$51N!a^Q^#|tem&zQ5SPO`jPdb=Dyy} zU#{VSJ%xwkaj$-p^yCE|Eq-3yR!pEkvbOuRw~Xg{0(CU}H)qa#ihh&)_%&!cX5g87 zb+*N?sU(?L1(>?%N)^_S+3|h=qNmzLnW|Cj&_PDu|?oRdDNpJuVJaa$c6sQg{(xhqoY{fvO!ccEv~csG*l z7hiHz&A;LT^M)h6JHkDx1UA1Z)BeR8^|~+FXxVYzC_j2-L$Qv@+uJetVl4TC;z;@0 zXT&5u%}ANh+mQ%#$1-Fg(96+0vs>$*u5?IZUy66PD8NIu0gBSiZw;|DeOF~wyuKU{ zcZ@jz!5&Mn=cRcieBKo@OQhE;idMeq|FaDpILqQ%R@9?wm$rxY=B}iUR}+>RV?_kF zC*Rz@G9z%AxhwA5fLkN%9x;}ffCndc7N^zFKSi5lM)UWD7?k3r6qR!3;+mzMMWOndWXjdB3*u6aRZs{LkZI&h3*AdV>SE`l=?2!=(zMYy% zL^tMG#pJ;BcDqd*@p0HgDuT58iS5CY9o6n`u|oX(Uu79GG}MgBfzUTk*8S_Pg3F`4 z8}h!aOavuq_O3@>zM@#Ss`Z=XGI5)1`mvx<;FW8m-5rzxqDy<0mKUf#^9QnI#Z_R{nPAsCsQ}|^E4%kCN`hEA!bj2O#8kK0d@Ta8xp-k0qa{0 zxg_NkqbukukGAr{T~(v~_!QLN6Nfhx6&YDKm-9~v^(Db(HX8acTD#v1KoLv>280M9CRwiDCxyscZhiwHCKBT^Lal@OHv_51ve;M9KmbrHBWW=nv|~lo8dk?@O3e+eOY0m z{U757Eq2grH+dibCQ-^dGaQyH_24;H_4l;m3?m(_aFWy!Amau6#Gr$+g`Ck?9d{&2zz3V zzLAt^^nM2>N2sHczj(c(;DQv(m=wCq-lOg~1!4>V{I}ivzeyBgV+$wFh&RK;DQk7- zF4KLang%M*J(Tt^P==QIA6_@+C%yS3+p$$e?(z*fph8c0hL89S0F_3)j$B(jWHS8DjKlE@H zw7}5U)D>>q?Oy^%E+(^^=~F7fK^~tUs33m+C2brSC^FUZr^GGOC&W06U6}#f^MBFCtWuY?;stMN?exmt zc=J8&r1zpJ?!fhZER|(+74wfsOEx#1D!qp*b0R-FJk?+9tonNVlUwKE_4&k;={LQ} zze$)yU3a&MwY~3Q{*Ct{cEI}<26NhWU0WVK;|0<&V*2%~rf@o{PB`HoT2Ee1`o@DC zVp_CDVzkga=8=v|k*=~kk*!TXX43>I;E)w~Jy^Oc*wNlJf{yxKQrJYV@ua`Eli)u6ACmofGd97#k^jY&O{8Q=PQ z(#TrctC_tqWbJlQf641L=NMD(65$)=y*Z>DQv1*LE>?mH`$#WN+njkrP2UP;1gcE? zN^hc)7!1z>Tly$V_y2*-&;f;SF84e)3Lx&;Q68RVubdHk74sMCzIRSyV0Z0a+W{;X z5bsR8i{b5ja9U7jS$2#0&R^2d&{_@{zNG(W7tux$t>fg5mokg)*LPyy#T@_9 zWS4rq*i6KV^TqPcbnXRCm({byu&v$vpV-EA5`B&`PKS5j`6d5w)j2O&*ll+x_)W6+ z{r>*N@=V;~U6s$jNp53|Q74cw`_`Jg7|68!KWj1Vsvf#Ll;eUgJMk(5UgE<*)Mm~! zcCXRkOuxU^Iw2Nw{A_OoidV5TSs0)XEZn|)3itghwvKBNThiA3o|sP`6_ss&@fjBy zR7%}C`%PjiTT6j+$mxx{n1_3x(DP9h5~3#{TE%$(@PA+f(1q1Af%b79NYCr0U%}q~ z%}<{0i2o*uL2~AI%(Vk+Xc9g1ykB|9V@IjK%?IH)sdBn$|57$wzd(LAM4R#pp2qYx zf@4f9y&T4g{a_SLY2ZbBjC5g;`-7XuxSrOQxY83>u}aRvf$6lrV14E&x{MKURQ|c& z?3Lj+b-6hT0-~L-V}#KUujGy2DLsFl7iUuB7JxG@d?ymnel4(3>YuhvPNTqONrek? zC?m^&6DAq-6<@P@932~b^FGzP>A0eO@wxM9#g)xo)Xe*st#;?8YRi9E!@M4}n0?*F zkVm4)mpb0H(}WiXiaK_YPZTC;X?3Qr_u>{>i!Jp!_g|)=p1lNb z)rH4ZX{E6nR!nqiks%^B-t#h6!Yi?1iyI;oYTpe*t7)PBiw zdTK@{w!BxH|&7s^sf@Czz1OtU>x;9&U)%z^y_?;Z43RmJYDi zl7BpxT(mC-R@Xw_%qJ-L@n(K+;y&?rD^$`m+1#HM1%fm(x;Uk|#NKzJb!igmrFvZ^ z3i1E2XMtM8VFzyr!JZuxd(C25{g2PPcZBZuI5_MZT!?-m5!sG-+!NZ5$bUW~SVzo& zNW!Qz{}*j)yhpTquZtP{_xtGch0lz6M&<1I=j!#h(#kS#Q3ovp2F-V79DQZtgaIa% z9ie9dIDg;2U}LGM-k*EKe&^5Y@FIKB7Ta(?{(QoCNS=7 zcN@QL57!i5>DSZU|M?dUdv^Ob$=6ScXU}rkxZQi|-Zwu=iz#*~G|s;+8{RtTt%iPS z_OsA+cj*n;0-V(gH!fDW_YZ8)@hc*FrtjI$%C=LzoJH+hhVj)$o}6-_0fr;q+{~Kg zM)3}<7hXbb)3lo3-{3Agd!@)dMFcZYY3@5zg@U*hY6JdC~a zGT`eR`l36og|v{^tV**;;=JLES3BY!1noTk7p#*WYs@yKT<8b~@U&ml_oHK)P_f!@ zGR)nZ^@~%5GlC&5d$srCSxnD8JHo$M;=ey@O#6$~SL}oXjKf=nL=_yrF1zyoC|Sg7 zc&(pJQm5H-NPo9q{Xp@;2{w7&Ii!1p{*x&^%)Ew@m@=Dl!?6s%B zop|uKs~Mlwf5CPQ3sb)8N^ZSwnz{nigR<5^fbFCzdl6BPe}qk{D8eYVTV^(u7p=L@ z_G}ca`cKJMeIKJ==KM4ZZdEW_LARYe7qb4ZSnoZ-|?{`|gB!P;{L0cif$SbfH)OWD@#_d#`nq zxcrjoYWBGm&0!KIdC!me-S+O=s|Mwy65kWq{G+N=%ppDO+6t(4wExe)5}TlpxG1MJ zwp|38T4%$24c~#@TSp7m2eZdS0!r8>Z)*-^{i!V?*q2Ujhm}FOy zm6&9gjFgO$l9c%K-z2*%#4NjlIu>%;ZrG`i-2Sz%nD_(4f3ob7lagw&d=-FP@w#hq z{_s$1`KtNOpUdFv;bENbVI2I=rL}m~{PX|0U^ln!+@Jp6E91L)-kJHv#mrC6FZc0h zRvR|4Hx4bnX`$bg{K{&2^;66J&iy%$^O>JotrzBdT7RB(?T!=qKmQ1iy)oJw_MZZX zoW%M5A{M%Fk#}kDzW8y%dETGGiRk|__UryaocPIo{C^+(KX3knn)!K&$OrLL*=4QV ziyh*ht3@|G9RL4({dsfak2I)DihrIXlK77Xh&){E z{71y|hC|6-#sB5t!u+%1|LDiz*DKfd8{fI(Mn~@FW_)M37}RQhd6pb!|DXFyY0o}a zX1pSPLtzec&bpM`Y43%V54B6Sh?4G{ikwgwS!2-UbJ!SW;#h4P$c1r83&Bhyd8}>d zVbB#iK6|eTXw{ri;k3OXUjDI!d>M_`SLtw82M!o>#z zi%G*6Aj?{)d&;HV7+J+V%~|z3D=J_tLnu@WSXjOm=f$8y#n= z=J2BoX{X-V6PFu=7|i(U4|QFZAN3w8h-5_0(0B%)(11*CB&X^?@tRMk8827ovy23< zfiVf)wGj72uXI=G6P8SoNpYyO^DCq>0bXBSf5gVjzz!w6tMSTBw`;vh`!V~plBgW! zu&d08Mf6T~!;*ZM;W4hYbgmT4Z4P-4wlHy;pcG%+Fm|;CMyOjKSsz+|LKiK2-NfNxV&F$>0Wfyx8nk}8_(UEmCsu{oh5lDRyPC6Vp+B&?K%wo>A)(>% z0>-Q2*zrlE5LGx}t6l7MVhRUXSA)h}WOc+YsoqNs@k0non3jYTwmv&k)CsoSe)TZ8 zb&M+mXaS=!5=iDq!4lc)L>vh*#S#9inSg2?}YBB{Lm6~;lNeT0Q>64sbMebLbIMu2!|60i`NLh}@eE;&x;>XIctH}~4Q zo6>!WTDD&|MUqd5!6SbhHg8JDwTK%6cRrIihyiv&lNg*Hcv!pW%`PkKI&jUb-mv(J zSfg=NQYo=jwBc(ANczxXqR+*VEaUG{p!JDpBfFZDLsw<&l4Z;jl99EUq3uG$s4NQ) zcdsHDmLbLZbig_y#Uj%Qz8adu=oyLx2d4vGSu{#1yrI4TMsae0gNFy}KVHdsee!q! zJGKgZ;mTvO3*Mobd za+3C5gW|eLECZ8AcAa(&%kS-Gh3Y*5%$pZzQ-~g04uT8QgIS3jYfRR}uh;)EA>EB1 zow4)<%;|j9>ucC{JLFUfSMYT=3JYs+dOmCLUBu;nPI3luflw#27JvaQOLfS*G9N`HDH-8g_Ti>>DMdG%fZ9;8msvoD9S1(AgmovR`%J<{ufO`*5jdM`F zv{17Q=CNfBO1r;rW{dBIe5=z|Dsmm#h*zVC+0Cq9`pbw(hH2a_go*k5Nfg~65KW(Y~yKd2{ z`khOhvWYBfuQxEl$Yh}dI-)^-fB|i8`&dAowtbq&lACRy8^^?`R;^D5#~v;?@?u8z zh0~Rsvy=q04%oe|pI-X%XE=ENE}^Pb{f;aBn)TH%!RA#?5=%*gzDp!^RxIo3U}8UL zQ()NzzU9R7V{*#LRW>qL)m9Vz2t41a*?iec{`!*i0}mK;aG3*}?dIKWL|5FOnj$i| z`P4j#z5gJV3;|8QqoZSG3x>R+%6S)W0~`ZC1%lKNJ(P4{r+W8H`Z$Lve=)b+@Svur zRDn9}v~)U;l9)KDCl|R8cFS%d24{czeA#g3#1D7CbykYNYoxGHa+)XWRn70x6sg_d}p>ou5Q7d<2%+~%p*wGQJjaBoO=2BwDt=BjG@DVpj}T)ZH0 z=Hbi_kWH`+ZIVk=;RnCMRFbLg&?zUq`f=qe_3GUwk`LrsG^9b-o5->@K{W-^{Z1U6 zS1=Z4$@`Qv9xTU0h|)r;y@5}Hg-xylf4WoMH4DT})G!S%#sJyX#5>U60bE+-ur&^q4&G3qlcY_bFCt09h;Q5wcm z28Iq?ZK7ZuX|7ix9A%$>xZGHbsZLG@*IO$gKF9|d7_)D9$bv6JJG{rh)yXAhk^mlw zLdH8R1z1ld6+?56hTsFX4vxBXE}WVS%-iXuwMsPG$id{$$iNN0#KsK5Fc6Wp>xWY_ zaglgq7}js?%es?+Q#wi`o_49W$xpt5`;yb;6A1^t320vLgbsyl(Zz8w;DV$u& z+6I)bW%u$*IR5cX!tXSuhz*jdo6hBf8CVr%tWK1~m?mK!E6TTR!#El!HhnpcR1&R4 z-Om^pecA#n8A76moluh8tJ+J*_*sxiY$A`w15(mX)>)iTb)m;DB+JHHO_rQ@Y`Faf zE^XeD81#tolFs93@U}8Wc=XgRPi(Bquy=)^YV?_0Vx7>PAr?sAY*VA`g;5EMHVSTk zoivM+0OoX)Tklj?WKG(5sL={Vb3^PA^nF5K61U|=be-gTph?)JYr?ZR4;m&Hc)ha4 z-YB$>aianr>NG#ZWiZmdE@juaylsQIW|_+4+P}xkQzL4`%Yl+qa+J4g_c4c2N9MqZGhc{{&Q> zJV9@L-C+;Q=-)2&e%_uTXTf7~28WS0tg(a%aJ7g-n+$_S?}5q$ijj{Yoa^|>)-ixR zA|Tvl&}CS~UE41?dO^wg{-ka{V|7KL{B?<|@uKmo*t|fjJxwRKd?qRttg*R-^`o(p zsNV)F3tuboOxgtzPG~)yhY=&ldTRi7y|DQ7@bfY~MB? zzGGIsfp{Xdz6p|HIn9fCmQcwxH^hmpnriN16EE5+x2@1cgyFmRa8=x zi&)gu`JS|tkieBftuWj=K3_I;){`B+!_@fVUFj*aktB?rw(K#W#Er4)vwL5F-eUK zr8^F;)!9bCEQFtLm*U-#`?1tINLzeg@T&>sleEaoL!eV*>Fa?Rp{=%5#)74K6a zkAy?7_%>@tUSb=8r*E6!>>T_Pn!MAK7-E!E-EBLCMxucy>nl3ERrl-PszJHtS|uI< zwueq6WRfqDU{=jO)W|bShhl`%1%~S_9+u2=OAM~&_v&>2fN+_9mMY^Dddxx2+^f@I zlUSAA96lT{Y58=SUliGvlE%l_qu9x{1bM&s|Nj_!@2Do5u5XlHLvNuANbg8jP(rU#rG;Lk6M7Mq8jubGK@cUOcR~$CdJ!T@ z??{4zG$~3Gg!_x$&-1SLJLmjyRuX3uZ$5rmi^_BEs_01?WurBE4O zw7k?D^M^QK^82AXWGZCpizv^nP!JHBj2riz@3Y3fHBQL?u5?}y5HhyIgjtKLTMcw9oQEC+JFIjYd zQ=$t&QhfiiBCG0M!eef7j;6@3iB^07vnPlgss z_2eV7B1dZlJWO=L#Y4p8yK-0@^|(?Yn1qy+abPtwij8Xmmb76!C#u{Bd6L)oD%wXU zf0qYpT?MLWoI$)YFiJU>bhc%rx+JGh%QapR&l99V%z(MVnMt_S(z@>I%l^EpsLEpC z)RpR>f}W?U;$2G#6<0TnwA#;aiOt`A6AB`SNkyKUFD6- zDo`)4w(Fs7#p1~aLUYQJku_Sw8acOFj8FBY-TK=_h}nYg2(`rLpjaAC*>g!Yhe^Be zMOK&`gDDL><~2fTzu~J8rbl z@}N^S9kSd^q;gV^YXjRaQ5c0R7%qVo9JGFGl3^kNvMS^pl_^o9@l2mjp;AU%iGiD#=}pg?ww`ij z&P|?BbTU7&XTF&*<$6;VLgP#H=9OdjD-*xT9j&I9F->eUK3DYLSR`Ovmc z_@mpido*#CpUkEAO#&)jt%$l{PzAZE$I9~xwoIB~puER9{I=S~W=Nx9-fXN-o9*j* z8t_9i#PRr&Pg^iIr7}%l(^8Nwq6k`)94h?x!$xkM6`(x1ZwJz{ffTX+=qsv@n?f#j zT2DqQ<1=VJGV!k#RullfcSF?0-M&W-ZsXYoajyOuIfo*`2sDyf>!*yrfK*bM_w~p{CZTo4vrh>OyWxT$*9N8wg@HYOO6;j5 z%EOW|!(CaGqV1JQSc=@h^wJ))cbxwF*&VLT#5U6iBS@RIy8L(heFG_Ca43WGDnqC^UEaDli7Zy-!kXadZR#)w(72`%PScEG1V3IHP( zQZ1DZTW0#;fpIZoccsbBzO-C9j51r}FY&b2>{cF5e9|jQt=PpmgNKXnt=UbC%iTg4 z=oNi@D60XVH$9Nhxro!QWVTeMZT(RGRE@c}7leqzfqgx9K6#&p;%j;|lpO)zWM+tLdJ`F^iyJ3%N$>ffh30#yX!yx#_$ALqV z?eyZh4V*7ix{b(4rTS_lTjt={XQ(A(wHLYmhl8&l7=XG=)ZuAXgBBQ+a|8?#llP%b z`WTzZ_F^DdiJi@Jh7Gp0Ipr3Yrfo^KPAy!|KBFvb%lxvOo5Uy{%2=0k%;CE2SL{SG z;&}DixQ*UzGS-bZOUs;#RA(`B+$1x02?@#(W8_+RwI+mk?>;}C`P-@mtF8x-ei+9g zj-FgwOav^5Q1YK9cm$_u2@1Yp5-}>NDQX5`-rXr45>L^c66)}4c1*K*e631741l~D z%!Q^7#|@T4D2Fjr~jZKmZomh!u*AvMW56UxPqjK*89L%1$N z7*!(Bu5jiwy%M53wYEn~$d>IYHXyc&{e@p{hKsB~B(=z5>5sSEmD!o+@pnw-F1%NF zD41Us2Te8|+;NkoN=XJ{Tcirar6&ZD0vgsPbHojOwlYj&nucE^8Gzl-kHoX#$4_8n zQLAB?Jg9);6LLLxj#=y=(w%d$W{PG~u_WS4$xkN$*jn|=VeU{Yky;9JX0^_na`WOYY6E~0#iDZLz^^GErb)RF zRXFQ<*_OGR1j8sqxMg=1&u@Dm&jy_bm@AFt6Dm;-Q^3jE0{Wr_DujeEldhohl4JnGn} z>LWWoOxceB=W;=BSV^`BP2$*25z3)olYW>oZlg@kz)DyDgYa-(flwKzF8`{_#-6ZM zouc2;gU^WfsQiv+J&zwpXO8yL>H672?>Iktr}16Wo+MRXK__ri9h2CvkgZHe_R6S^ zaeGv=4T^-*zj;{2ZK_)OR0r)#X72HC@-oZ2v92jGZ<%()yxI5T8(M65?*m2|oU$K{D#jPQQn2_9zO%Nv$gOCu=h1IGL0U-iniy`aspGYW)`Karv_NaInmDC1u% zHY#cg@@VG(UyoBp8LS>0%cMmC-*gNqL1GG(g#2{E2TwpLh|AnLO zj|Y^naBW?WDtC<#*&p(#bdrKZc41Y#I19GV$si@g?2#(I zdUD=nzN-&AecAA+5v9w<>5vV)I>Sewqy73h<;43v*{u;6C>}mw;PC`Tw3QjxViQ{; zLT%F_^h}o;2tQJ1a!8f~aY#@zN<(U<@;zK*6LX4P)G?^ZGw|ZbwiP37%ZTV_;(CDA zdIL718ZCy@hkkz{k~oa>1N;Rik^@lS+%tx)4wO;GziYtUV)2ra3$Yu+qbgKFdiKM^ zxybD?3T^`Zl!#;zDftuwuQjy+KVGX*l7IQt@#c+%_$+S)7DLU1medC&gqCz%gmdKH z(1EzwG&$rY!1dI$Sej%g&#+ZF4g*_nQaq2;9~rk6}BUTsWLmi{rldY7_!Qc+xGLN=11+oY?bMsRUBvmCAvX0w}DoQme7Gh>j@)KH)&ySHqEPA9 zqd-^}E@mQjrW)~NVJhk=rvnEPu+>kBQ!( ziM0VJR3tt#NuLs+@6~CAKjT&yk1DHZEEM5;6sn zsg@{SodSuBBvKe}4gkvrph8izN&%(n&g+Q=re`J=*^7lmdGv6{U~W^UJ0!y%Rvtu2 zdj1Usos@BTlK9D9WFT7bCCvE!w((GM0ZP%&p()NbMQf~xM5Z2Kj0J$QpefE!e(@mV zU(4T9@M&VBX!q4?g%@#g#d5I5O97{nNLcI{6|<>qoc-dQ8?wfr&Q#0+H;js6l{q4l zoO4mm7`QGO2R4JlqHT+25z+JPy-K`a|hIVTf3s<8c*}*jTWtpogv*YHn zaW)Y~bBlgxr8+l(sFb50Wdf7&i64n|l(#$&<{_QIBTlKsIb>F&!m7`{mWSNNm)Vum z-;#1lAk2vnh@%ox1{Zc8s#d1aGRZvZHxhfu2h3p;_853PmOhMR5TK zI+;FeiwLPzGupT$>KHOtv+C}9PreIbdd+~yAS)NP%~1$v5Y*{g!!ik^S5)%fp5oS7 zv_>tH@j2&a4&Ld-*^|5i;mO#P6h`SJ<2I$hvSg{@)D)L;*<@`KfQf;JiHFPLMl#zF zmGPNyB#yWM9nFlifvdDdBRWkg;&iTRURyFNJ*=j=hlfxwTeFK%9LWdw!7wTWx>DZS zB|MQ}u`a!u&DW5%7dLV4cNiy9Gpc|XQR=gm;mkbS4;$eFaWV``PvFBZR)FVh3`W9C zr1{&@JxmnklEmr>@Kjr1i=efHQ>QS6xE)2lW~qiKNtBprE6I%SKx;`;8T zn!;?7eDwP^5h5db7JyeerT18zGhIgFS5~FnC&B!E$JoXkY1+VT0Gh5kE{{T9 zff8O>GArcUG6d(XB`%7I518@O4==mFklzx!^8xL!gnRz$ zjE5AAQGQ4KLbsRH^+vi8m4aDhkRfGpfV#T*5TBF{?G(mhB4@PemFsQMS`IMur4^eO zEp$YJh_~?;EWv<20c1W$U&uHi{QP@S0H$6)8Hjpbf?FxPHck|Rz%OuT?R`T$NlzH) zk=5cEC_7Iht07akP=I0D?Y2;&Z1Yizn8$>)!+oo#62J~KL{}Ok&jGDR1r-6vT#^FQ zDxGvDuMw=+^i5APu{m%Oku1W(BsX}?ov;=BHF08Oyq+u&j@$fDo6;^hwaKrWNGlmv zx`r})-E({gXyiApw3yWE>sa!JTgJxJId*LzHefFeXj~S@of)CQVxjyXD|%%#E!I$m zHb>5PD(3!nf+}MAP%O;1r%VQCo9@G#>31wQG1j|WPuL2Pw+N4T3|U4hZ|#V%d0wP? z0%Z5~iH*}MbjBQCB8SOkBh!#tgO3t@Q`k7+sS&Xt4rx$|CL#fT%DcF)o1~{xL+@dj zgm@eCN`_G+$5)`;_Wi&or2VnyJK{7%ZFRDG^4DZwa&iK1j%c*aA8o7NWlrcHSY<8| z&zTk;$dm9BjfL`1dr~oxgIZW?`*SH}JZy%X$-B7nn~_PKIqy=~RLeza;((d0!xo&{ z{%(vWAaik{N)R`RC>sfLQ;VT-%bcCh+}Zc19wv?JI8Q(*^AXg%bv#*gta1%E3EzP~ zk~JQNYbv>=XijC8qjUQKI6niS%k)l8iWG^>ym%E(r)0P~${XaPD@V|ZDAPC-is-ix zHS=;H>KqoRZ1&kEW)%WY4mrJYb33$R-MfS}IQ$slFSGS#Ig?OPH!;&RWHhmtDO$(P z7|L3hR+g#Zd~3;4OA_0CN#em=WCkyF<2Zd>GDtQ-#72spwscH_y=hP`!i2Ip%LWtYLhfb}T+e&!kM-+jT^-LRb>HHJ)NeiIc) zV3(aI6MKn=RRI&sGDt=f1322a63i&tI6|`1lmT!Kj+3uT@;W9cp@;8>S#c4z2)v~= zfl5UO~0^Sx!3ajl`qP8N|TDo1k84H{3#4K7tO-+ zty-l0B#v%dlKND$WE(FV4|BLST?V*(u@dsA5NCrVxxZutQ^k1H7BI83VCms6&f3CDM4bH3I_~Jh0A7(y6N#=YW5)Lx5 zI0y51!MAP1^o9_dBPn0pZol`z%@yKZ%<=NH=?Uzjali!X1nJw}#_{hcUge0h$A~_n zf5|oPGHEdO7#cr$CWAZsd2C2FgGd&#n#xWVsxpbJgyeAwQn@6s(-OQ{*F}^k_8$jU z{Am?EPFw!Usn(27Ba-Nl*`7C*ls|hLO=?1IqF+?c#;jFogBfmC50>__DnbsAs-YI= zC^f5U`d=8b%8)xoTEBD&6cbN6?uQH`dulsuWk8#v&g_qMS%2^j?YN}Zz=kKv%{M!GsGi!B37r)$>-xqkODSd0fG1GHCe$9AhJ3h{D z@Gj%Ho9YOg_T=r$q&>oiNWalZtMc39VTiZRZ=DOaT~BTczsuXVHRM)1Ay*yr8}6bE zkP#L2Xx_ek#dt+7U2e87dqv(w4#k;fRq#V;9vChna|HHpUr~ipg;I*aD;C>bP|&x| zUDPoE>gb)Cr^&wz_GvUutamfaRj>9V50Se)qtz-Vy9sMtm8YN*kXB|oj8sZ%_xW*N z6u_bQapjeC*jx9IPfJDW*C{e9`z|Z{k)Il`aOG4-Yp=LUaNquuZ2ptnQyr8PKU2|O zJmq#ecW{q9Y*qdxHttKDZGPVfK8QhB)aregn-*_rdf zvky;nt$$0h;P8H;bU0UQ9NdraFWⅅ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%0Drt< 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�Ck}L8>D-WL$1A=pGm_bYPHu1MKdFWj; zya@7#bY1uaUK<7sq!I{#?$ZGY0P9jeE%|+l{O@F59{PZ25fTan0F@ayY~5b8inc9i zt1UmqG*T=ZW6VXDkHV|)?0f?x4gYsQ(2T6AR~GLB4Uya@8C%vko_Gb3jzbQk5rjCb{;r|_1bXtcwj00gt$*3S2Eat0DS6~DM&$O-H zUEBTc>Sm|osVvXF6l;o>yF4SGm?(-DzF;-&5DW+!k^s6EV5w!?<^W8vb84T8vzwH( zi(`t5i#v;FtCF#Y2+2W3XsR3luk|%!N$4Mw@vP1>K}npRtu`qfm-4`=D06cZG&6-gH9 z6*(1!HHtIS;c|mIAe5wra~8l2y3P*(q$sz?x-iRG28J$mBDS5Uepgi|ahst(MhoS# z3M4FP9l=o01tW(o03#e$pVS)>AymqKVTzezgeWOl{hRS$k>E|1F}&u_&XBuK3v0a_I&|zzEpH0+8X>3D*FF$4d<-2VTc|3`y++K{oSw0VBf84&PCQC3Z+TnZ8Re*kYC-#Y*R literal 0 HcmV?d00001 diff --git a/resources/icon_clear_cache.png b/resources/icon_clear_cache.png new file mode 100644 index 0000000000000000000000000000000000000000..f1f1ae6e74eedc03900fadb2222b277507814faf GIT binary patch literal 2476 zcmcImdo+}b7T@3Jo53(;GE!oUR8$@fB@@QvE!7OCBb0{ZG9g0`@~BT<37Jk(Xfmhr zsFa?hMy8Y$p-2=(j&i6F88zZM=lpx`fA_B6AA9e$Uu&hS9k_B2zfk$kiz`=J|tqmi}Pu9xzr?C=A#%^l;2y;2B-fs zieeF?bG6K;lGEpE&5)H%ktzKtfrtCh;2f>4>#bO`(a8go@(Uin+nq>Ee%JA=yiWpo zfpHxNIxf9Ysgj>7cZ6dw%R=&&*WRUDWsDxixM9&%K3?$r%|hG-R2FRGdHrKxuxv5P zrQoT6tXCFSJ7h`^EJ!#*jL|wZ53C(e5RAlmUn{B`mB&ZbqOKpM<#3g=>fQSNb)v2( z8d$Eid1;m(o(@HraLTNX7UNC5L`&}voSm54<_uv9f;>@a@CDZL_9uLVs>v217K~V@ zy?M`^h#o9MB{3NWTD@|8o_Tt>%})R`!my@i@wU4!C%wTD1Szb41$tC_k3~4acjDE8 zN4aegj4Ty;8JGw-TL$*8*2ILiQ0(=xJnzc;Z1_s(HXP!I_?{GOg71jn@TE7~-dtiX z!sEbFDmmI3MN`C@_e1qi^A}xxfeVNT!`7o#J|DkZo8pZ~blPH_vTwiTG%8_26sPQZ z;#jr$RY&l|`>z;Kmq{9>+N2B$7S<`>KQhKY#wnE1GcFGj7PzqZP z*7=tTrbp5rHIc+NFu>&h$-s+K~1r4QE_IC{aP9fn6X zrG#mVKG14brsQ>+;6`ws(NwF`k`w7nDvV>#q__mk>%Yf$>o{q*G@UThCV6RgMC5Ca zjYZsbj(@q&WgMF)Tc8oR1#7lTAvAZT;=K&os$MBf`mX8d9vYVVd(J6 z=A(40LwCxTPbBUm#4_sA*ZK;W6T!!Ir^iebZS`=;Y^B^s#PNtD?o&04@oRc7<(XM3 zQayjvMF&r`yGz4s9^BPtGj^#mP4H5n_OxBim#i{!gYVVMF-5d)4qlNO#f0|k5GeSD zP`nj}DGc9P;xq4)g#4i7*=bK{cvjkA4181lK-i!k635*U3pb}q+L`m)-a0=jr$8v5 zf4XsnEYMS4HgUCoA8Oh1KoLh2ljj=aiskoPV!Q?$lH5q{&);x$Zt%cuCe z!Xso>`}df@q5{^`?&EC-Q-{o~{KG^nmNRr$COx}vYZ3QAcTW6+?Pv1c;|W4DuzYgS zL0|TkKaq6MJv07&RAkC64fX(Zqq0SYmpi&Yr(08d4?XuaJ}pz3xmp;1ysL$`fYCA7 zVkFvrdGLGKvTszj6LhP_GnzgRWlbiLDVlNeXq!SG65Sl2>7kaHS?WQD(rFXho%7-) zdHPG+xR8tDYi`X8P<+K<_WzNRCaWs&IrN-`)*(0pQwTj>_`zh2`1bZ)$;tAYHkGSeNQQl#yXr@KOT{)kH$;>1?|SvxG!?fVs>YDgzm z|FF!uk;r1zejQGVU=A3ZAxvTl? znJK!}Q9}j%)$NoL@8o+-_xho&<3E;Tm}D~q0@%(ZkcTWL`(C&lay))ue(kO~>g=VE z5vh(K)oSc06fag*@_JfXXD_X=!LsK==n{H&!jeqsU?{dv0dHwA@Y_|&*RAC!=u+AH zLAnt|Ek-;uj*6FmR$8WuSJd3+9wG3AYIjBa_y8rV;`<1D@Ym)&q*4P^7Yo)JB@>M z=6HI-@Z2rM(J!P%<&E90dZ-})uHV^q^Ge07)-iQ<4}f;B|3#xflil9yPNXs|dYh>1 z1lVy+i4CY44j3Jf|E%s|;0G>lD&h9|^+fF*4Wme@P|h147`GdmDhZ>o7vYKIGMVN+ znez+-E<{!~cK&K?g!ljLNhX(>8vSFSaAFQpq9nD)XK1pSchD*t1taRg$ez`{VpHB+XR%6@e{1R}x)#~6Wt@_b1+a^uW n{~rRGFjd1T{{>6=Q;JW#OH|&8E#A+3Vc_bx(cy}n-=Tj4A7ug6 literal 0 HcmV?d00001 diff --git a/resources/icon_music_albums.png b/resources/icon_music_albums.png new file mode 100644 index 0000000000000000000000000000000000000000..ea4d1800517ae6cfa25fabf735fe254a1a6bf9bf GIT binary patch literal 2216 zcmc(h`#;l*AICrY?BebmvgMu}%?Ly4B$siH87HI>xva^Cb54<(SQ(a1O&&ALr6`1% zBA3z;2O;G;j7Z5M(Q?gYNv`|)?fd=y1?Tbn;r;&Q`FQ>Ie!p&bdJvT4HRS;SP;zx~ zA_4$pXN3T=u$}QOHfSCIWY4)eIeOFI&F7NYCkQ&srfbn~KN0^V-nZsVvb*eSq9&>Y zjrg6p{n-CbOV!zlqr6uvXM>cLg8r|3D+6xVue`X4g;$3PES}EVzrF6X_vCxN%M+MY zlm+HSiD!(;(;Ig7o|ye}?_;xRU$SF`tM6-`h4rrWbk`i9iSrH&aP{z^Y507g@Lz>E z0$MG@WYp0{AHuSrwvLoWWFL3((uz-fPeYpkl`2xeBtZj;8ku#!}F$PH{Z4@zAZwsVs&V6*32wH5k7xEW&EII;S;K zkAJ1v@q|V9==tYT1h{e%bKAXa#r~mxjG(mV+>H+t~Gd0Ti z>3C!eLvCaGv|*T9el+~MJTCa47qmAr&+e>PDDN2yidu!WxJ=rEIv-2tQkN z;5FzmHE!fF!~$00wCGSRb*g(;>qxUEV%T#}jOU67><%^5(P)#()LqS+e**8wh(K7D z$4}+o`{Iu+URMjU6h6r`}9J&B5Ye8+hp2~Jf>XA<4dggdY1n=G-DzcHBHEr6DC$~lu2x_l?82B^Xz;h3{ z$%Oa6tYTi|KuSm8-ND>jNnKw}Xotghd)ghspV;baK?g8rssYIwB?^1f&;&D4vNwR~ zO-HvJglW>-Q29qpn{C&5flm6T?OA6bRbg>OXOgTQjiU56P(g_MiMn5V(myTr%2)Sm=v`d(SR z+CGnN?lMw>Ej+wJWHb1E#A&i?i^gw52I-YYBt=wr%Z!#`vXPCnLhZ5+sTr4rRDWJM zrTgU6=S-_5!3T@BKkUPHbCER&U#Y~_Dq`h@?3rOz`ZYh%49UTVanO8AcStf0GaW>H zo7ComY{C2$Xj6OFRHCY`0z=_@G`u5u*s&y`#l^2ZWpP5A=gm|VsdA%}@~TqfZWQDc zdNb1)hxEdQuxHXt#>p!tZ!{6c4$j?*E*qiO38|mZJ3cpMh(o1+Jz3m#id}7^k^$@V z>hvj3an!26bsK7!Xe@>n^?`~qMO#sL!kBg>Uj?3YocRw@N5?Xob_x=#7UYU2V3liM zHL29OCz=dbSmnkS`m6yJW`NdDfPuqGHkzM>mHd53P+jzQ+b z2#Lg+rC^QKQKbf#0M2n^2RUV#;svSz_RqN>uf_CdEDF|nkDQkD<|A?+k{-WDPLMGs zb8p;{(|0MwGRw(u0p^>asj{QRr?*(Yd)p(I7ot%v>PY#!iXCR%`=z-N_e^#kY-`>9 ztNbXK6Pz%(G%uu9TEJ@)^B(Xf#_b8p3?fT2Fb0S_%2`&)yxkLhoHq)#|TK_z!FZzn`{cW zi5;wFuQj#mZwH*;es8Yt0{qOzMC}G5+3mcfiKVKCIMtc3fd}Ddd+e^4AD;{G?lAlC z(Ip5w*7ewgJTVgTZJPtJQ~~Zx3&KA)Up4u>HQU<+7U?qs+EH}H$eRaDLdmZy>rz{* zmqy523P70O^U=~%=dt@MC<~GB4z_|VvDGHA+_vS~JJYhuT2MweuYtQZ*k?WT*e-L^ zM%Il4)TYcwQPrgZQ8Mluhh@97D2!ag1OA2Mw#P2Sl=rM7%i*;yT3XX?D>#z^)_w3c zw;KLH({Q@*>KmfX*lbi^<}WKFpmzzp_N8rnM`=E*1G}gifEFqBIR+JhfXHcgu86ec zx9i)$i;4{2&Ew2cg~*#eUqYi=B!qr3FCGm@+KX=tYlMTKk<)4=*pnOia0V*n?v-+lmB#|}+* zc1=!6KUvmbC{l|D7`5egy_oHKC(d2LCnJZZgHjbjvQcxy8GC732TgHNPWb|>T6p?M zVzgn!Fe)fx!;8`O)aTOOjpc{ii~xY?Jk;NlgF(?t4}Dgr%3vpL)FT#8mA(B=;etv3 dDlU3bs1Rw$C6tuu>@*O-74P9x@k>zRe*o9j=!gIS literal 0 HcmV?d00001 diff --git a/resources/icon_music_artists.png b/resources/icon_music_artists.png new file mode 100644 index 0000000000000000000000000000000000000000..c9db24d32148e5fb8944e85ebce1eaed98813e4b GIT binary patch literal 2958 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 UVWZc6{n>!kIa_nOsn`Ag0M;J|I{*Lx literal 0 HcmV?d00001 diff --git a/resources/icon_music_explore.png b/resources/icon_music_explore.png new file mode 100644 index 0000000000000000000000000000000000000000..a50170e3b1ef89db910cf6a830ae54e88ece1675 GIT binary patch literal 4769 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 literal 0 HcmV?d00001 diff --git a/resources/icon_music_library.png b/resources/icon_music_library.png new file mode 100644 index 0000000000000000000000000000000000000000..2ed2bc88f888d9654a02e38735dbd165531e33f6 GIT binary patch literal 3159 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 CU5Ok3 literal 0 HcmV?d00001 diff --git a/resources/icon_music_top_artists.png b/resources/icon_music_top_artists.png new file mode 100644 index 0000000000000000000000000000000000000000..617d9610bc55abca7cc6f5268688397871bb699e GIT binary patch literal 1404 zcmV-?1%vvDP)F%tl~jdGpZGheRQpeQC9OHt5Ei{p%$}=Jsf0=pZSpwu$MV0 zUag1`?&fYpS(++3bFh~W_>rF(;~;z3q%F>zY6&~3F$r*p4a3u1Qio8BHX7aE;S!d( zMIWBV1`ZL11sXfGl8mU1g!q_t404YabfCHouN=IIZ)jx5VIzXv!$F4yk4Hk8oYiCe z?wI}wY=0Kc(F1tOIkpvx@x;;jJC6CMI-M=9$!icW`JQJApqmAz6#>+^I=5iDU$PZ^ z?;5Vb_{U-=(*W+7&o>)fUEia^FWD^|at(I{`Xyr?`k5B6-4J~muedr6VT)g~5xSGO z2z_VqJJSSS8KQ@H(bcgZ5x-??7Moqu9zeF9_7&5|I(=0(4!C+XYqR`cv6CEe4Ay}a z`XMOwL6vAUL|?%UN2d{-%%pS`z-e#4qApoyx!V`nR%4w-9ltvGG@~FhRZxO3X zA$|~r3^U)7HIMsIj;Vem%oLRKGKNT&iy#{d5y4VqAqyMP$2QbsQG)zT4ErE^SSPgz z^=v~QHX;jISc(W1B3p#zB!+kyl+-=a?>EoQ~aXGeN=sWYPgIw6KT>TGOIB z9GBr*m@f>!_M=Dy`PhKjB3NT;@dMo9eXZu9+mhw92rAgZ;vCCxZ;a$#8tS!Jfd~@pc$*)|_ z=Uq$uE_R_EuakK!@vqTMfpId6S;kV+6XtIWvy`P=?OR7^Ow@gTFo9r*5uQOS*L#wEN8%}VGWaWZta`Lf;Jn8R zElVm|Z2mPwSK+DQ*0&j6pclR99W7W^A&EOInD|o|GmnWzX9*0Um&B(eCET-(JeEx) z)$I<_aGgozo-hpTZ2b)}teU7xwcb(1o-n-3o#f1h5y!sPkD`g7I>&hiWBd-_nbhBD z=w#mbAAx%=$v+uhXE<5P@VBeJIWyk-y3IpeC7LVhN!%IL3?5qSn|%~ImQ;gY<09-& zknJSNc+c|wq#Tuw<3rYS0)yM}`fDVDKRs8(#;vD$@9XFg1Apj53Pjv>uKDPUL&-`# z%|dIks-iz7RsHCUeI2;LF$ZoaO{$=vprD|jprD|jprD}epT)loKi}9|5SBs!0000< KMNUMnLSTZ&s+S%B literal 0 HcmV?d00001 diff --git a/resources/icon_music_top_tracks.png b/resources/icon_music_top_tracks.png new file mode 100644 index 0000000000000000000000000000000000000000..010b021d27a4ba10648bba5955d7e955bc857f27 GIT binary patch literal 301 zcmeAS@N?(olHy`uVBq!ia0vp^VIa)G0wn*(udD%5Up!qLLo)8Yy|%uW$x))MkVUAl ziAg}D!9hVlTQi~CK}kT{Qy~0+Crgu%fXK!qZ-)SeB-W(K-))N>V;W`$f1YS_-sHpS zC$sJM$_WBN;5?g6TiLnAbRsq+9Bg96%D54~W*>L$s*90x*M{}-&0{(8>#%g}Uxlj+ zH=JKsULdmO`TB*88?;I{-)CAKG&gwFr{LwCA-hlX=U!T}t75`zEAKm@*%RyIr*~di z&(WSe)#dIkdB)$*C02w=K6`rUBkPvSagROzH#U~_>*;SjxS;$)K=xIu|7#{|pLO!l e1Ud2V?)+WK7e8k#>aPO&pTX1B&t;ucLK6UXe0ux< literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8c7d83b51d5f4913c2b850488b82b5ade7f40dc1 GIT binary patch literal 5879 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 literal 0 HcmV?d00001 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 = '

Tx)_>j)$$p{sL4`ScN= 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
SZgal#T%)tT7BsoS+S4I!mJ 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