code time

This commit is contained in:
cere 2024-02-21 00:35:31 -05:00
parent 789a5e0b02
commit ae28da8d60
153 changed files with 56768 additions and 1 deletions

BIN
resources/fanart.jpg Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 153 KiB

BIN
resources/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 964 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 B

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Binary file not shown.

View file

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

50
resources/lib/__init__.py Normal file
View file

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

View file

@ -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/<track_id>', 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<start>[0-9]+?)-(?P<end>[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()

View file

@ -0,0 +1,154 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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",
]

View file

@ -0,0 +1,67 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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",
]

View file

@ -0,0 +1,62 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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",
]

View file

@ -0,0 +1,129 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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

View file

@ -0,0 +1,107 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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)

View file

@ -0,0 +1,61 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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)

View file

@ -0,0 +1,153 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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")

View file

@ -0,0 +1,63 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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,
},
)

View file

@ -0,0 +1,41 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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)

View file

@ -0,0 +1,60 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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()

View file

@ -0,0 +1,153 @@
# defusedxml
#
# Copyright (c) 2013 by Christian Heimes <christian@python.org>
# 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

View file

@ -0,0 +1,4 @@
import os
import sys
sys.path.insert(1, os.path.join(os.path.dirname(__file__)))

4417
resources/lib/deps/bottle.py Executable file

File diff suppressed because it is too large Load diff

View file

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

View file

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

View file

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

File diff suppressed because it is too large Load diff

View file

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

File diff suppressed because it is too large Load diff

View file

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

View file

@ -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"""<?xml version='1.0' encoding='utf-8'?>
<playercorefactory>
<!-- This file created by the '{self.__plugin_name}' addon. -->
<rules name="system rules">
<rule name="http" protocols="http" player="VideoPlayer" />
</rules>
</playercorefactory>
"""
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

View file

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

View file

@ -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("<f", buffer.read(4))[0],
struct.unpack("<f", buffer.read(4))[0],
struct.unpack("<f", buffer.read(4))[0],
struct.unpack("<f", buffer.read(4))[0])
def get_factor(self, normalisation_pregain) -> 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)

View file

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

View file

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

View file

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

View file

@ -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")

View file

@ -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:
"""

File diff suppressed because it is too large Load diff

View file

@ -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("<I", len(key))
for i in range(0, len(key), 4):
self.r[self.keyp] = \
self.r[self.keyp] ^ \
struct.unpack("<I", key[i: i + 4])[0]
self.cycle()
for i in range(self.n):
self.crc[i] = self.r[i]
self.diffuse()
for i in range(self.n):
self.r[i] ^= self.crc[i]
def key(self, key: bytes) -> 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)

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

File diff suppressed because one or more lines are too long

View file

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

File diff suppressed because one or more lines are too long

View file

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

View file

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

File diff suppressed because it is too large Load diff

File diff suppressed because one or more lines are too long

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

Some files were not shown because too many files have changed in this diff Show more