gmusicapi-12.1.1/ 0000755 0001750 0001750 00000000000 13512455420 014052 5 ustar simon simon 0000000 0000000 gmusicapi-12.1.1/HISTORY.rst 0000644 0001750 0001750 00000034307 13512455274 015763 0 ustar simon simon 0000000 0000000 .. :changelog:
History
-------
As of 1.0.0, `semantic versioning `__ is used.
12.1.1
++++++
released 2019-07-13
- open default log file as utf-8 to avoid windows encoding problems
12.1.0
++++++
released 2019-04-20
- deprecate Mobileclient.get_promoted_songs in favor of Mobileclient.get_top_songs to clarify its behavior. Functionality remains the same.
12.0.0
++++++
released 2019-01-08
- breaking: remove gmusicapi.clients.OAUTH_FILEPATH. Use Musicmanager.OAUTH_FILEPATH instead.
11.1.1
++++++
released 2018-12-04
- add back gmusicapi.clients.OAUTH_FILEPATH after it was removed in 11.1.0 (a breaking change)
- deprecate gmusicapi.clients.OAUTH_FILEPATH in favor of Musicmanager.OAUTH_FILEPATH
11.1.0
++++++
released 2018-11-30
- add Mobileclient OAuth support (``perform_oauth`` and ``oauth_login``) and deprecate email/password auth, which Google now often rejects.
It works the same way as the Musicmanager; see `the docs
`__ for an example.
- update Mobileclient.search schema
11.0.4
++++++
released 2018-11-19
- fix a number of bugs with Mobileclient.search
11.0.3
++++++
released 2018-09-19
- fix an "__init__() takes at most 4..." warning coming from oauth2client
11.0.2
++++++
released 2018-09-09
- fix validation of "ios:..." format device ids
- add inLibrary field to station docs
11.0.1
++++++
released 2018-03-18
- update schemas
11.0.0
++++++
released 2017-12-09
- breaking: list calls now default to max_results=None, increasing the default number of results from 100 to 999
- add updated_after param to song/playlist listing to support differential updates
- add support for free radio stations
- add filepath+extension to unsupported file exception message
- fix "I'm Feeling Lucky" station never refreshing its seed
- fix crashes caused by some 503s during uploading
- fix gmtools for https://github.com/simon-weber/Google-Music-Playlist-Importer
- fix AAC and ALAC content type upload detection
- blacklist requests 2.8.2
- improve id documentation
- update schemas
10.1.2
++++++
released 2017-04-03
- validate device ids to prevent 403s during streaming
- fix LocalUnboundError during login for some environments
- update schemas
10.1.1
++++++
released 2017-02-10
- deprecate include_deleted param to greatly speed up responses for Mobileclient.get_all_*
- Mobileclient.search now works on non-subscription accounts
- fix logging IOError on read-only filesystems
- fix problems caused by broken requests IDNA support
10.1.0
++++++
released 2016-10-31
- deprecate the Webclient
- add podcast support to Mobileclient:
- get_all_podcast_series
- get_all_podcast_episodes
- add_podcast_series
- delete_podcast_series
- edit_podcast_series
- get_podcast_episode_stream_url
- get_podcast_episode_info
- get_podcast_series_info
- get_browse_podcast_hierarchy
- get_browse_podcast_series
- add Mobileclient.add_store_tracks
- add Mobileclient.rate_songs
- add Musicmanager.get_quota
- fix get_all_user_playlist_contents hanging for large playlists
- fix is_authenticated status after uploader_id exceptions
- fix upload progress tracker remaining after upload
- various internal improvements and schema updates
10.0.1
++++++
released 2016-06-04
- switch to pycryptodomex
- minor schema adjustments
10.0.0
++++++
released 2016-05-01
- breaking: Mobileclient.search_all_access is now Mobileclient.search
- breaking: Mobileclient.add_aa_track is now Mobileclient.add_store_track
- add situation_hits and video_hits to Mobclient.search
- add methods Mobileclient.deauthorize_device, .get_listen_now_items, and .get_listen_now_situations
- add property Mobileclient.is_subscribed
- add playlists and curated stations as station seeds
- add params locale and subscription to Mobileclient.login
- add param enable_transcoding to Musicmanager.upload
- update to newer Google apis, returning more data in responses
- reduce memory usage during uploading
- fix a variety of bugs, mostly python2/3 type errors
9.0.0
+++++
released 2016-03-05
- breaking: attempting to reupload a file after changing only its tags will result in a rejection as a duplicate upload (it used to upload successfully)
- fix webclient login after Google changes
- fix ``'str' object has no attribute 'refresh'``
- prevent upstream protobufs TypeError by locking version
- a 'matched' value may be returned even if matching is not enabled if we were unable to disallow matching
8.0.0
+++++
released 2016-02-08
- breaking: drop support for python < 2.7.9
- add (experimental) python 3 support!
- add Musicmanager.get_purchased_songs
- add station_hits to search_all_access results
- add disc_number and total_disc_count to Musicmanager.get_uploaded_songs
- add a prompt for device id in tests
- upgrade gpsoauth, removing dependency on pycrypto
- deprecate Webclient.create_playlist and Webclient.get_registered_devices
- fix various packaging problems
- fix KeyError in Mobileclient.get_station_tracks
- fix a TypeError from requests
- fix various bits of the docs
7.0.0
+++++
released 2015-09-19
- breaking: python 2.6 is no longer supported
- breaking: webclient.get_registered_devices has a slightly different schema
- fix Webclient authentication and get_stream_urls
- fix MusicManager uploading: Google shut down the rupio endpoint
- fix certificate validation
- fix album artist metadata not being upload
6.0.0
+++++
released 2015-06-20
- fix creation of multiple android devices from android_id=None; support creating device ids from mac address.
- android_id is now optional for mobileclient.get_stream_url, defaulting to android_id from login()
5.0.0
+++++
released 2015-06-02
- breaking: Webclient.login temporarily broken after clientlogin deprecation
- breaking: Mobileclient.get_thumbs_up_songs renamed to mobileclient.get_promoted_songs
- breaking: Mobileclient.change_playlist_name is now edit_playlist
- fix Mobileclient.login breakage due to clientlogin deprecation
- fix Mobileclient.get_genres: return a list and handle invalid parent genres
- add support for filtering out recently played station tracks to Mobileclient.get_station_tracks
- add public playlist results to Mobileclient.search_all_access
- add Mobileclient.get_registered_devices
- add quality option to Mobileclient.get_stream_url
- add support for public playlist creation to Mobileclient.create_playlist
- make optional description param for Webclient.create_playlist
- better handle locating mp3 transcoder
4.0.0
+++++
released 2014-06-08
- breaking: remove webclient.change_song_metadata; use mobileclient.change_song_metadata instead
- breaking: remove webclient.get_all_songs; use mobileclient.get_all_songs instead
- breaking: remove webclient.get_playlist_songs; use mobileclient.get_all_user_playlist_contents instead
- breaking: remove webclient.get_all_playlist_ids; use mobileclient.get_all_user_playlists instead
- breaking: webclient.upload_album_art now returns a url to the uploaded image
- breaking: due to backend changes, mobileclient.change_song_metadata can only change ratings
- add mobileclient.get_thumbs_up_songs
- add mobileclient.increment_song_playcount
- add webclient.create_playlist, which is capable of creating public playlists
- add webclient.get_shared_playlist_info
3.1.0
+++++
released 2014-01-20
- add verify_ssl option to client init
- greatly loosen dependency version requirements
3.0.1
+++++
released 2013-12-11
- remove extraneous logging introduced in 3.0.0 -- this could have logged auth details, so it's recommended to delete old logs
3.0.0
+++++
released 2013-11-03
- Musicmanager.get_all_songs is now Musicmanager.get_uploaded_songs
- Mobileclient.get_all_playlist_contents is now Mobileclient.get_all_user_playlist_contents, and will no longer return results for subscribed playlists
- add Mobileclient.get_shared_playlist_contents
- add Mobileclient.reorder_playlist_entry
- add Mobileclient.change_song_metadata
- add Mobileclient.get_album_info
- add Mobileclient.get_track_info
- add Mobileclient.get_genres
- compatibility fixes
2.0.0
+++++
released 2013-08-01
- remove broken Webclient.{create_playlist, change_playlist, copy_playlist, search, change_playlist_name}
- add Mobileclient; this will slowly replace most of the Webclient, so prefer it when possible
- add support for streaming All Access songs
- add Webclient.get_registered_devices
- add a toggle to turn off validation per client
- raise an exception when a song dictionary is passed instead of an id
1.2.0
+++++
released 2013-05-16
- add support for listing/downloading songs with the Musicmanager.
When possible, this should be preferred to the Webclient's method, since
it does not have a download quota.
- fix a bug where the string representing a machine's mac
was not properly formed for use as an uploader_id.
This will cause another machine to be registered for some users;
the old device can be identified from its lack of a version number.
- verify user-provided uploader_ids
1.1.0
+++++
released 2013-04-19
- get_all_songs can optionally return a generator
- compatibility updates for AddPlaylist call
- log to appdirs.user_log_dir by default
- add open_browser param to perform_oauth
1.0.0
+++++
released 2013-04-02
- breaking: Api has been split into Webclient and Musicmanager
- breaking: semantic versioning (previous versions removed from PyPi)
- Music Manager OAuth support
- faster uploading when matching is disabled
- faster login
2013.03.04
++++++++++
- add artistMatchedId to metadata
- tests are no longer a mess
2013.02.27
++++++++++
- add support for uploading album art (`docs
`__)
- add support for .m4b files
- add CancelUploadJobs call (not exposed in api yet)
- Python 2.6 compatibility
- reduced peak memory usage when uploading
- logging improvements
- improved error messages when uploading
2013.02.15
++++++++++
- user now controls logging (`docs
`__)
- documentation overhaul
2013.02.14
++++++++++
- fix international logins
2013.02.12
++++++++++
- fix packaging issues
2013.02.11
++++++++++
- improve handling of strange metadata when uploading
- add a dependency on `dateutil `__
2013.02.09
++++++++++
- breaking: upload returns a 3-tuple (`docs
`__)
- breaking: get_all_playlist_ids always returns lists of ids; remove always_id_lists option
(`docs `__)
- breaking: remove suppress_failure option in Api.__init__
- breaking: copy_playlist ``orig_id`` argument renamed to ``playlist_id`` (`docs
`__)
- new: report_incorrect_match (only useful for Music Manager uploads) (`docs
`__)
- uploading fixed
- avconv replaces ffmpeg
- scan and match is supported
- huge code improvements
2013.01.05
++++++++++
- compatibility update for playlist mutation
- various metadata compatibility updates
2012.11.09
++++++++++
- bugfix: support for uploading uppercase filenames (Tom Graham)
- bugfix: fix typo in multidownload validation, and add test
2012.08.31
++++++++++
- metadata compatibility updates (storeId, lastPlayed)
- fix uploading of unicode filenames without tags
2012.05.04
++++++++++
- update allowed rating values to 1-5 (David Dooling)
- update metajamId to matchedId (David Dooling)
- fix broken expectation about disc/track numbering metadata
2012.04.03
++++++++++
- change to the 3-clause BSD license
- add Kevin Kwok to AUTHORS
2012.04.01
++++++++++
- improve code in example.py
- support uploading of all Google-supported formats: m4a, ogg, flac, wma, mp3. Non-mp3 are transcoded to 320kbs abr mp3 using ffmpeg
- introduce dependency on ffmpeg. for non-mp3 uploading, it needs to be in path and have the needed transcoders available
- get_playlists is now get_all_playlist_ids, and is faster
- add an exception CallFailure. Api functions raise it if the server says their request failed
- add suppress_failure (default False) option to Api.__init__()
- change_playlist now returns the changed playlistId (pid)
- change_song_metadata now returns a list of changed songIds (sids)
- create_playlist now returns the new pid
- delete_playlist now returns the deleted pid
- delete_songs now returns a list of deleted sids
- change_playlist now returns the pid of the playlist - which may differ from the one passed in
- add_songs_to_playlist now returns a list of (sid, new playlistEntryId aka eid) tuples of added songs
- remove_songs_from_playlist now returns a list of removed (sid, eid) pairs
- search dictionary is now flattened, without the "results" key. see documentation for example
2012.03.27
++++++++++
- package for pip/pypi
- add AUTHORS file
- remove session.py; the sessions are now just api.PlaySession (Darryl Pogue)
- protocol.Metadata_Expectations.get_expectation will return UnknownExpectation when queried for unknown keys; this should prevent future problems
- add immutable 'subjectToCuration' and 'metajamId' fields - use unknown
2012.03.16
++++++++++
- add change_playlist for playlist modifications
- get_playlists supports multiple playlists of the same name by returning lists of playlist ids. By default, it will return a single string (the id) for unique playlist names; see the always_id_lists parameter.
- api.login now attempts to bump Music Manager authentication first, bypassing browser emulation. This allows for much faster authentication.
- urls updated for the change to Google Play Music
- remove_songs_from_playlist now takes (playlist_id, song_ids), for consistency with other playlist mutations
2012.03.04
++++++++++
- change name to gmusicapi to avoid ambiguity
- change delete_song and remove_song_from_playlist to delete_songs and remove_songs_from_playlist, for consistency with other functions
- add verification of WC json responses
- setup a sane branch model. see http://nvie.com/posts/a-successful-git-branching-model/
- improve logging
gmusicapi-12.1.1/LICENSE 0000664 0001750 0001750 00000002737 13374625453 015105 0 ustar simon simon 0000000 0000000 Copyright (c) 2018, Simon Weber
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of the contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
gmusicapi-12.1.1/MANIFEST.in 0000600 0001750 0001750 00000000422 12667715127 015613 0 ustar simon simon 0000000 0000000 include HISTORY.rst LICENSE example.py README.rst
recursive-include gmusicapi *.py
include docs/Makefile
recursive-include docs/source *.py *.rst
include gmusicapi/test/audiotest*
include gmusicapi/test/imagetest*
include gmusicapi/test/*.jsarray
global-exclude _local_*.py
gmusicapi-12.1.1/PKG-INFO 0000644 0001750 0001750 00000060411 13512455420 015151 0 ustar simon simon 0000000 0000000 Metadata-Version: 1.1
Name: gmusicapi
Version: 12.1.1
Summary: An unofficial api for Google Play Music.
Home-page: http://pypi.python.org/pypi/gmusicapi/
Author: Simon Weber
Author-email: simon@simonmweber.com
License: Copyright (c) 2018, Simon Weber
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
* Neither the name of the copyright holder nor the
names of the contributors may be used to endorse or promote products
derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY
DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Description: gmusicapi: an unofficial API for Google Play Music
==================================================
gmusicapi allows control of
`Google Music `__ with Python.
.. code-block:: python
from gmusicapi import Mobileclient
api = Mobileclient()
# after running api.perform_oauth() once:
api.oauth_login('')
# => True
library = api.get_all_songs()
sweet_track_ids = [track['id'] for track in library
if track['artist'] == 'The Cat Empire']
playlist_id = api.create_playlist('Rad muzak')
api.add_songs_to_playlist(playlist_id, sweet_track_ids)
**gmusicapi is not supported nor endorsed by Google.**
That said, it's actively maintained, and powers a bunch of cool projects:
- alternate clients, including
`one designed for the visually impaired `__,
`a web-based jukebox which ships with its own server `__,
`command line `__
`clients `__,
`a FUSE filesystem `__,
and `an Alexa skill `__
- library management tools for
`syncing tracks `__,
`syncing playlists `__,
and `migrating to a different account `__
- proxies for media players, such as
`gmusicproxy `__ and
`gmusicprocurator `__,
as well as plugins for
`Mopidy `__,
`Squeezebox `__ and
`Tizonia `__.
- enhancements like `autoplaylists / smart playlists `__
Getting started
---------------
Start with `the usage docs `__, which will guide you through installation and the available apis.
Once you're up and running, you can explore the rest of the docs at http://unofficial-google-music-api.readthedocs.io.
If the documentation doesn't answer your questions, or you just want to get
in touch, either `drop by #gmusicapi on Freenode
`__ or shoot me an email.
Status and updates
------------------
|build_status| |repominder_status|
.. |build_status| image:: https://travis-ci.org/simon-weber/gmusicapi.png?branch=develop
:target: https://travis-ci.org/simon-weber/gmusicapi
.. |repominder_status| image:: https://img.shields.io/badge/dynamic/json.svg?label=release&query=%24.status&maxAge=43200&uri=https%3A%2F%2Fwww.repominder.com%2Fbadge%2FeyJyZXBvX2lkIjogMTEsICJ1c2VyX2lkIjogMn0%3D%2F&link=https%3A%2F%2Fwww.repominder.com%2F
:target: https://www.repominder.com
* November 2018: proper OAuth support for the mobileclient.
* February 2016: Python 3 support!
* September 2015: Google switched to a new music uploading endpoint, breaking uploading for outdated versions of gmusicapi.
* June 2015: Full mobileclient and webclient functionality was restored.
* May 2015: Limited mobileclient functionality was restored.
* April 2015: Google deprecated clientlogin, breaking both the webclient and mobileclient.
* November 2013: I started working fulltime at Venmo, meaning this project is back to night and weekend development.
For fine-grained development updates, follow me on Twitter:
`@simonmweber `__.
.. :changelog:
History
-------
As of 1.0.0, `semantic versioning `__ is used.
12.1.1
++++++
released 2019-07-13
- open default log file as utf-8 to avoid windows encoding problems
12.1.0
++++++
released 2019-04-20
- deprecate Mobileclient.get_promoted_songs in favor of Mobileclient.get_top_songs to clarify its behavior. Functionality remains the same.
12.0.0
++++++
released 2019-01-08
- breaking: remove gmusicapi.clients.OAUTH_FILEPATH. Use Musicmanager.OAUTH_FILEPATH instead.
11.1.1
++++++
released 2018-12-04
- add back gmusicapi.clients.OAUTH_FILEPATH after it was removed in 11.1.0 (a breaking change)
- deprecate gmusicapi.clients.OAUTH_FILEPATH in favor of Musicmanager.OAUTH_FILEPATH
11.1.0
++++++
released 2018-11-30
- add Mobileclient OAuth support (``perform_oauth`` and ``oauth_login``) and deprecate email/password auth, which Google now often rejects.
It works the same way as the Musicmanager; see `the docs
`__ for an example.
- update Mobileclient.search schema
11.0.4
++++++
released 2018-11-19
- fix a number of bugs with Mobileclient.search
11.0.3
++++++
released 2018-09-19
- fix an "__init__() takes at most 4..." warning coming from oauth2client
11.0.2
++++++
released 2018-09-09
- fix validation of "ios:..." format device ids
- add inLibrary field to station docs
11.0.1
++++++
released 2018-03-18
- update schemas
11.0.0
++++++
released 2017-12-09
- breaking: list calls now default to max_results=None, increasing the default number of results from 100 to 999
- add updated_after param to song/playlist listing to support differential updates
- add support for free radio stations
- add filepath+extension to unsupported file exception message
- fix "I'm Feeling Lucky" station never refreshing its seed
- fix crashes caused by some 503s during uploading
- fix gmtools for https://github.com/simon-weber/Google-Music-Playlist-Importer
- fix AAC and ALAC content type upload detection
- blacklist requests 2.8.2
- improve id documentation
- update schemas
10.1.2
++++++
released 2017-04-03
- validate device ids to prevent 403s during streaming
- fix LocalUnboundError during login for some environments
- update schemas
10.1.1
++++++
released 2017-02-10
- deprecate include_deleted param to greatly speed up responses for Mobileclient.get_all_*
- Mobileclient.search now works on non-subscription accounts
- fix logging IOError on read-only filesystems
- fix problems caused by broken requests IDNA support
10.1.0
++++++
released 2016-10-31
- deprecate the Webclient
- add podcast support to Mobileclient:
- get_all_podcast_series
- get_all_podcast_episodes
- add_podcast_series
- delete_podcast_series
- edit_podcast_series
- get_podcast_episode_stream_url
- get_podcast_episode_info
- get_podcast_series_info
- get_browse_podcast_hierarchy
- get_browse_podcast_series
- add Mobileclient.add_store_tracks
- add Mobileclient.rate_songs
- add Musicmanager.get_quota
- fix get_all_user_playlist_contents hanging for large playlists
- fix is_authenticated status after uploader_id exceptions
- fix upload progress tracker remaining after upload
- various internal improvements and schema updates
10.0.1
++++++
released 2016-06-04
- switch to pycryptodomex
- minor schema adjustments
10.0.0
++++++
released 2016-05-01
- breaking: Mobileclient.search_all_access is now Mobileclient.search
- breaking: Mobileclient.add_aa_track is now Mobileclient.add_store_track
- add situation_hits and video_hits to Mobclient.search
- add methods Mobileclient.deauthorize_device, .get_listen_now_items, and .get_listen_now_situations
- add property Mobileclient.is_subscribed
- add playlists and curated stations as station seeds
- add params locale and subscription to Mobileclient.login
- add param enable_transcoding to Musicmanager.upload
- update to newer Google apis, returning more data in responses
- reduce memory usage during uploading
- fix a variety of bugs, mostly python2/3 type errors
9.0.0
+++++
released 2016-03-05
- breaking: attempting to reupload a file after changing only its tags will result in a rejection as a duplicate upload (it used to upload successfully)
- fix webclient login after Google changes
- fix ``'str' object has no attribute 'refresh'``
- prevent upstream protobufs TypeError by locking version
- a 'matched' value may be returned even if matching is not enabled if we were unable to disallow matching
8.0.0
+++++
released 2016-02-08
- breaking: drop support for python < 2.7.9
- add (experimental) python 3 support!
- add Musicmanager.get_purchased_songs
- add station_hits to search_all_access results
- add disc_number and total_disc_count to Musicmanager.get_uploaded_songs
- add a prompt for device id in tests
- upgrade gpsoauth, removing dependency on pycrypto
- deprecate Webclient.create_playlist and Webclient.get_registered_devices
- fix various packaging problems
- fix KeyError in Mobileclient.get_station_tracks
- fix a TypeError from requests
- fix various bits of the docs
7.0.0
+++++
released 2015-09-19
- breaking: python 2.6 is no longer supported
- breaking: webclient.get_registered_devices has a slightly different schema
- fix Webclient authentication and get_stream_urls
- fix MusicManager uploading: Google shut down the rupio endpoint
- fix certificate validation
- fix album artist metadata not being upload
6.0.0
+++++
released 2015-06-20
- fix creation of multiple android devices from android_id=None; support creating device ids from mac address.
- android_id is now optional for mobileclient.get_stream_url, defaulting to android_id from login()
5.0.0
+++++
released 2015-06-02
- breaking: Webclient.login temporarily broken after clientlogin deprecation
- breaking: Mobileclient.get_thumbs_up_songs renamed to mobileclient.get_promoted_songs
- breaking: Mobileclient.change_playlist_name is now edit_playlist
- fix Mobileclient.login breakage due to clientlogin deprecation
- fix Mobileclient.get_genres: return a list and handle invalid parent genres
- add support for filtering out recently played station tracks to Mobileclient.get_station_tracks
- add public playlist results to Mobileclient.search_all_access
- add Mobileclient.get_registered_devices
- add quality option to Mobileclient.get_stream_url
- add support for public playlist creation to Mobileclient.create_playlist
- make optional description param for Webclient.create_playlist
- better handle locating mp3 transcoder
4.0.0
+++++
released 2014-06-08
- breaking: remove webclient.change_song_metadata; use mobileclient.change_song_metadata instead
- breaking: remove webclient.get_all_songs; use mobileclient.get_all_songs instead
- breaking: remove webclient.get_playlist_songs; use mobileclient.get_all_user_playlist_contents instead
- breaking: remove webclient.get_all_playlist_ids; use mobileclient.get_all_user_playlists instead
- breaking: webclient.upload_album_art now returns a url to the uploaded image
- breaking: due to backend changes, mobileclient.change_song_metadata can only change ratings
- add mobileclient.get_thumbs_up_songs
- add mobileclient.increment_song_playcount
- add webclient.create_playlist, which is capable of creating public playlists
- add webclient.get_shared_playlist_info
3.1.0
+++++
released 2014-01-20
- add verify_ssl option to client init
- greatly loosen dependency version requirements
3.0.1
+++++
released 2013-12-11
- remove extraneous logging introduced in 3.0.0 -- this could have logged auth details, so it's recommended to delete old logs
3.0.0
+++++
released 2013-11-03
- Musicmanager.get_all_songs is now Musicmanager.get_uploaded_songs
- Mobileclient.get_all_playlist_contents is now Mobileclient.get_all_user_playlist_contents, and will no longer return results for subscribed playlists
- add Mobileclient.get_shared_playlist_contents
- add Mobileclient.reorder_playlist_entry
- add Mobileclient.change_song_metadata
- add Mobileclient.get_album_info
- add Mobileclient.get_track_info
- add Mobileclient.get_genres
- compatibility fixes
2.0.0
+++++
released 2013-08-01
- remove broken Webclient.{create_playlist, change_playlist, copy_playlist, search, change_playlist_name}
- add Mobileclient; this will slowly replace most of the Webclient, so prefer it when possible
- add support for streaming All Access songs
- add Webclient.get_registered_devices
- add a toggle to turn off validation per client
- raise an exception when a song dictionary is passed instead of an id
1.2.0
+++++
released 2013-05-16
- add support for listing/downloading songs with the Musicmanager.
When possible, this should be preferred to the Webclient's method, since
it does not have a download quota.
- fix a bug where the string representing a machine's mac
was not properly formed for use as an uploader_id.
This will cause another machine to be registered for some users;
the old device can be identified from its lack of a version number.
- verify user-provided uploader_ids
1.1.0
+++++
released 2013-04-19
- get_all_songs can optionally return a generator
- compatibility updates for AddPlaylist call
- log to appdirs.user_log_dir by default
- add open_browser param to perform_oauth
1.0.0
+++++
released 2013-04-02
- breaking: Api has been split into Webclient and Musicmanager
- breaking: semantic versioning (previous versions removed from PyPi)
- Music Manager OAuth support
- faster uploading when matching is disabled
- faster login
2013.03.04
++++++++++
- add artistMatchedId to metadata
- tests are no longer a mess
2013.02.27
++++++++++
- add support for uploading album art (`docs
`__)
- add support for .m4b files
- add CancelUploadJobs call (not exposed in api yet)
- Python 2.6 compatibility
- reduced peak memory usage when uploading
- logging improvements
- improved error messages when uploading
2013.02.15
++++++++++
- user now controls logging (`docs
`__)
- documentation overhaul
2013.02.14
++++++++++
- fix international logins
2013.02.12
++++++++++
- fix packaging issues
2013.02.11
++++++++++
- improve handling of strange metadata when uploading
- add a dependency on `dateutil `__
2013.02.09
++++++++++
- breaking: upload returns a 3-tuple (`docs
`__)
- breaking: get_all_playlist_ids always returns lists of ids; remove always_id_lists option
(`docs `__)
- breaking: remove suppress_failure option in Api.__init__
- breaking: copy_playlist ``orig_id`` argument renamed to ``playlist_id`` (`docs
`__)
- new: report_incorrect_match (only useful for Music Manager uploads) (`docs
`__)
- uploading fixed
- avconv replaces ffmpeg
- scan and match is supported
- huge code improvements
2013.01.05
++++++++++
- compatibility update for playlist mutation
- various metadata compatibility updates
2012.11.09
++++++++++
- bugfix: support for uploading uppercase filenames (Tom Graham)
- bugfix: fix typo in multidownload validation, and add test
2012.08.31
++++++++++
- metadata compatibility updates (storeId, lastPlayed)
- fix uploading of unicode filenames without tags
2012.05.04
++++++++++
- update allowed rating values to 1-5 (David Dooling)
- update metajamId to matchedId (David Dooling)
- fix broken expectation about disc/track numbering metadata
2012.04.03
++++++++++
- change to the 3-clause BSD license
- add Kevin Kwok to AUTHORS
2012.04.01
++++++++++
- improve code in example.py
- support uploading of all Google-supported formats: m4a, ogg, flac, wma, mp3. Non-mp3 are transcoded to 320kbs abr mp3 using ffmpeg
- introduce dependency on ffmpeg. for non-mp3 uploading, it needs to be in path and have the needed transcoders available
- get_playlists is now get_all_playlist_ids, and is faster
- add an exception CallFailure. Api functions raise it if the server says their request failed
- add suppress_failure (default False) option to Api.__init__()
- change_playlist now returns the changed playlistId (pid)
- change_song_metadata now returns a list of changed songIds (sids)
- create_playlist now returns the new pid
- delete_playlist now returns the deleted pid
- delete_songs now returns a list of deleted sids
- change_playlist now returns the pid of the playlist - which may differ from the one passed in
- add_songs_to_playlist now returns a list of (sid, new playlistEntryId aka eid) tuples of added songs
- remove_songs_from_playlist now returns a list of removed (sid, eid) pairs
- search dictionary is now flattened, without the "results" key. see documentation for example
2012.03.27
++++++++++
- package for pip/pypi
- add AUTHORS file
- remove session.py; the sessions are now just api.PlaySession (Darryl Pogue)
- protocol.Metadata_Expectations.get_expectation will return UnknownExpectation when queried for unknown keys; this should prevent future problems
- add immutable 'subjectToCuration' and 'metajamId' fields - use unknown
2012.03.16
++++++++++
- add change_playlist for playlist modifications
- get_playlists supports multiple playlists of the same name by returning lists of playlist ids. By default, it will return a single string (the id) for unique playlist names; see the always_id_lists parameter.
- api.login now attempts to bump Music Manager authentication first, bypassing browser emulation. This allows for much faster authentication.
- urls updated for the change to Google Play Music
- remove_songs_from_playlist now takes (playlist_id, song_ids), for consistency with other playlist mutations
2012.03.04
++++++++++
- change name to gmusicapi to avoid ambiguity
- change delete_song and remove_song_from_playlist to delete_songs and remove_songs_from_playlist, for consistency with other functions
- add verification of WC json responses
- setup a sane branch model. see http://nvie.com/posts/a-successful-git-branching-model/
- improve logging
Platform: UNKNOWN
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: BSD License
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 2.7
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.4
Classifier: Programming Language :: Python :: 3.5
Classifier: Topic :: Internet :: WWW/HTTP
Classifier: Topic :: Multimedia :: Sound/Audio
Classifier: Topic :: Software Development :: Libraries :: Python Modules
gmusicapi-12.1.1/README.rst 0000664 0001750 0001750 00000007321 13415264447 015557 0 ustar simon simon 0000000 0000000 gmusicapi: an unofficial API for Google Play Music
==================================================
gmusicapi allows control of
`Google Music `__ with Python.
.. code-block:: python
from gmusicapi import Mobileclient
api = Mobileclient()
# after running api.perform_oauth() once:
api.oauth_login('')
# => True
library = api.get_all_songs()
sweet_track_ids = [track['id'] for track in library
if track['artist'] == 'The Cat Empire']
playlist_id = api.create_playlist('Rad muzak')
api.add_songs_to_playlist(playlist_id, sweet_track_ids)
**gmusicapi is not supported nor endorsed by Google.**
That said, it's actively maintained, and powers a bunch of cool projects:
- alternate clients, including
`one designed for the visually impaired `__,
`a web-based jukebox which ships with its own server `__,
`command line `__
`clients `__,
`a FUSE filesystem `__,
and `an Alexa skill `__
- library management tools for
`syncing tracks `__,
`syncing playlists `__,
and `migrating to a different account `__
- proxies for media players, such as
`gmusicproxy `__ and
`gmusicprocurator `__,
as well as plugins for
`Mopidy `__,
`Squeezebox `__ and
`Tizonia `__.
- enhancements like `autoplaylists / smart playlists `__
Getting started
---------------
Start with `the usage docs `__, which will guide you through installation and the available apis.
Once you're up and running, you can explore the rest of the docs at http://unofficial-google-music-api.readthedocs.io.
If the documentation doesn't answer your questions, or you just want to get
in touch, either `drop by #gmusicapi on Freenode
`__ or shoot me an email.
Status and updates
------------------
|build_status| |repominder_status|
.. |build_status| image:: https://travis-ci.org/simon-weber/gmusicapi.png?branch=develop
:target: https://travis-ci.org/simon-weber/gmusicapi
.. |repominder_status| image:: https://img.shields.io/badge/dynamic/json.svg?label=release&query=%24.status&maxAge=43200&uri=https%3A%2F%2Fwww.repominder.com%2Fbadge%2FeyJyZXBvX2lkIjogMTEsICJ1c2VyX2lkIjogMn0%3D%2F&link=https%3A%2F%2Fwww.repominder.com%2F
:target: https://www.repominder.com
* November 2018: proper OAuth support for the mobileclient.
* February 2016: Python 3 support!
* September 2015: Google switched to a new music uploading endpoint, breaking uploading for outdated versions of gmusicapi.
* June 2015: Full mobileclient and webclient functionality was restored.
* May 2015: Limited mobileclient functionality was restored.
* April 2015: Google deprecated clientlogin, breaking both the webclient and mobileclient.
* November 2013: I started working fulltime at Venmo, meaning this project is back to night and weekend development.
For fine-grained development updates, follow me on Twitter:
`@simonmweber `__.
gmusicapi-12.1.1/docs/ 0000755 0001750 0001750 00000000000 13512455420 015002 5 ustar simon simon 0000000 0000000 gmusicapi-12.1.1/docs/Makefile 0000600 0001750 0001750 00000013015 12667715127 016447 0 ustar simon simon 0000000 0000000 # Makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
PAPER =
BUILDDIR = build
# Internal variables.
PAPEROPT_a4 = -D latex_paper_size=a4
PAPEROPT_letter = -D latex_paper_size=letter
ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
# the i18n builder cannot share the environment and doctrees with the others
I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source
.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext
help:
@echo "Please use \`make ' where is one of"
@echo " html to make standalone HTML files"
@echo " dirhtml to make HTML files named index.html in directories"
@echo " singlehtml to make a single large HTML file"
@echo " pickle to make pickle files"
@echo " json to make JSON files"
@echo " htmlhelp to make HTML files and a HTML help project"
@echo " qthelp to make HTML files and a qthelp project"
@echo " devhelp to make HTML files and a Devhelp project"
@echo " epub to make an epub"
@echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter"
@echo " latexpdf to make LaTeX files and run them through pdflatex"
@echo " text to make text files"
@echo " man to make manual pages"
@echo " texinfo to make Texinfo files"
@echo " info to make Texinfo files and run them through makeinfo"
@echo " gettext to make PO message catalogs"
@echo " changes to make an overview of all changed/added/deprecated items"
@echo " linkcheck to check all external links for integrity"
@echo " doctest to run all doctests embedded in the documentation (if enabled)"
clean:
-rm -rf $(BUILDDIR)/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/html."
dirhtml:
$(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml
@echo
@echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml."
singlehtml:
$(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml
@echo
@echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml."
pickle:
$(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle
@echo
@echo "Build finished; now you can process the pickle files."
json:
$(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json
@echo
@echo "Build finished; now you can process the JSON files."
htmlhelp:
$(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp
@echo
@echo "Build finished; now you can run HTML Help Workshop with the" \
".hhp project file in $(BUILDDIR)/htmlhelp."
qthelp:
$(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp
@echo
@echo "Build finished; now you can run "qcollectiongenerator" with the" \
".qhcp project file in $(BUILDDIR)/qthelp, like this:"
@echo "# qcollectiongenerator $(BUILDDIR)/qthelp/UnofficialGoogleMusicApi.qhcp"
@echo "To view the help file:"
@echo "# assistant -collectionFile $(BUILDDIR)/qthelp/UnofficialGoogleMusicApi.qhc"
devhelp:
$(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp
@echo
@echo "Build finished."
@echo "To view the help file:"
@echo "# mkdir -p $$HOME/.local/share/devhelp/UnofficialGoogleMusicApi"
@echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/UnofficialGoogleMusicApi"
@echo "# devhelp"
epub:
$(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub
@echo
@echo "Build finished. The epub file is in $(BUILDDIR)/epub."
latex:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo
@echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex."
@echo "Run \`make' in that directory to run these through (pdf)latex" \
"(use \`make latexpdf' here to do that automatically)."
latexpdf:
$(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex
@echo "Running LaTeX files through pdflatex..."
$(MAKE) -C $(BUILDDIR)/latex all-pdf
@echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex."
text:
$(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text
@echo
@echo "Build finished. The text files are in $(BUILDDIR)/text."
man:
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man
@echo
@echo "Build finished. The manual pages are in $(BUILDDIR)/man."
texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo
@echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo."
@echo "Run \`make' in that directory to run these through makeinfo" \
"(use \`make info' here to do that automatically)."
info:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo
@echo "Running Texinfo files through makeinfo..."
make -C $(BUILDDIR)/texinfo info
@echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo."
gettext:
$(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale
@echo
@echo "Build finished. The message catalogs are in $(BUILDDIR)/locale."
changes:
$(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes
@echo
@echo "The overview file is in $(BUILDDIR)/changes."
linkcheck:
$(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck
@echo
@echo "Link check complete; look for any errors in the above output " \
"or in $(BUILDDIR)/linkcheck/output.txt."
doctest:
$(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest
@echo "Testing of doctests in the sources finished, look at the " \
"results in $(BUILDDIR)/doctest/output.txt."
gmusicapi-12.1.1/docs/source/ 0000755 0001750 0001750 00000000000 13512455420 016302 5 ustar simon simon 0000000 0000000 gmusicapi-12.1.1/docs/source/conf.py 0000664 0001750 0001750 00000017614 13374625453 017627 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
#
# Unofficial Google Music Api documentation build configuration file, created by
# sphinx-quickstart on Sat Feb 25 00:36:45 2012.
#
# This file is execfile()d with the current directory set to its containing dir.
#
# Note that not all possible configuration values are present in this
# autogenerated file.
#
# All configuration values have a default; values that are commented out
# serve to show the default.
import os
import sys
sys.path.insert(0, os.path.join(os.path.dirname(__file__), os.pardir, os.pardir))
on_rtd = os.environ.get('READTHEDOCS', None) == 'True'
from gmusicapi import __version__
# -- General configuration -----------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be extensions
# coming with Sphinx (named 'sphinx.ext.*') or your custom ones.
extensions = ["sphinx.ext.autodoc"]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix of source filenames.
source_suffix = '.rst'
# The encoding of source files.
#source_encoding = 'utf-8-sig'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'gmusicapi'
copyright = u'2016 Simon Weber'
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
version = __version__
# The full version, including alpha/beta/rc tags.
release = __version__
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#language = None
# There are two options for replacing |today|: either, you set today to some
# non-false value, then it is used:
#today = ''
# Else, today_fmt is used as the format for a strftime call.
#today_fmt = '%B %d, %Y'
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = []
# The reST default role (used for this markup: `text`) to use for all documents.
#default_role = None
# If true, '()' will be appended to :func: etc. cross-reference text.
#add_function_parentheses = True
# If true, the current module name will be prepended to all description
# unit titles (such as .. function::).
#add_module_names = True
# If true, sectionauthor and moduleauthor directives will be shown in the
# output. They are ignored by default.
#show_authors = False
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# A list of ignored prefixes for module index sorting.
#modindex_common_prefix = []
# -- Options for HTML output ---------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
if not on_rtd:
# Try to use the ReadTheDocs theme if installed.
# Default to the default alabaster theme if not.
try:
import sphinx_rtd_theme
html_theme = 'sphinx_rtd_theme'
html_theme_path = [sphinx_rtd_theme.get_html_theme_path()]
except ImportError:
html_theme = 'alabaster'
else:
# Set theme to 'default' for ReadTheDocs.
html_theme = 'default'
# Add any paths that contain custom themes here, relative to this directory.
#html_theme_path = []
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#html_theme_options = {}
# The name for this set of Sphinx documents. If None, it defaults to
# " v documentation".
#html_title = None
# A shorter title for the navigation bar. Default is the same as html_title.
#html_short_title = None
# The name of an image file (relative to this directory) to place at the top
# of the sidebar.
#html_logo = None
# The name of an image file (within the static path) to use as favicon of the
# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large.
#html_favicon = None
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
# html_static_path = ['_static']
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.
#html_last_updated_fmt = '%b %d, %Y'
# If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities.
#html_use_smartypants = True
# Custom sidebar templates, maps document names to template names.
#html_sidebars = {}
# Additional templates that should be rendered to pages, maps page names to
# template names.
#html_additional_pages = {}
# If false, no module index is generated.
#html_domain_indices = True
# If false, no index is generated.
#html_use_index = True
# If true, the index is split into individual pages for each letter.
#html_split_index = False
# If true, links to the reST sources are added to the pages.
#html_show_sourcelink = True
# If true, "Created using Sphinx" is shown in the HTML footer. Default is True.
#html_show_sphinx = True
# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True.
#html_show_copyright = True
# If true, an OpenSearch description file will be output, and all pages will
# contain a tag referring to it. The value of this option must be the
# base URL from which the finished HTML is served.
#html_use_opensearch = ''
# This is the file name suffix for HTML files (e.g. ".xhtml").
#html_file_suffix = None
# Output file base name for HTML help builder.
htmlhelp_basename = 'UnofficialGoogleMusicApidoc'
# -- Options for LaTeX output --------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#'preamble': '',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title, author, documentclass [howto/manual]).
latex_documents = [
('index', 'gmusicapi.tex', u'gmusicapi Documentation',
u'Simon Weber', 'manual'),
]
# The name of an image file (relative to this directory) to place at the top of
# the title page.
#latex_logo = None
# For "manual" documents, if this is true, then toplevel headings are parts,
# not chapters.
#latex_use_parts = False
# If true, show page references after internal links.
#latex_show_pagerefs = False
# If true, show URL addresses after external links.
#latex_show_urls = False
# Documents to append as an appendix to all manuals.
#latex_appendices = []
# If false, no module index is generated.
#latex_domain_indices = True
# -- Options for manual page output --------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('index', 'gmusicapi', u'gmusicapi Documentation',
[u'Simon Weber'], 1)
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output ------------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
('index', 'gmusicapi', u'gmusicapi Documentation',
u'Simon Weber', 'gmusicapi', 'One line description of project.',
'Miscellaneous'),
]
# Documents to append as an appendix to all manuals.
#texinfo_appendices = []
# If false, no module index is generated.
#texinfo_domain_indices = True
# How to display URL addresses: 'footnote', 'no', or 'inline'.
#texinfo_show_urls = 'footnote'
gmusicapi-12.1.1/docs/source/contributing.rst 0000600 0001750 0001750 00000001365 12667715127 021555 0 ustar simon simon 0000000 0000000 .. _contributing:
Contributing to gmusicapi
=========================
The easiest way to start contributing is to help `triage issues
`__.
Development
-----------
Please make pull requests to the ``develop`` branch.
``master`` is currently used to hold the code of the most recent release.
Building the docs locally is straightforward.
First, make sure Sphinx is installed: ``$ pip install sphinx``.
Next, simply do ``$ cd gmusicapi/docs`` followed by ``$ make html``.
If there weren't any problems, the docs are now in ``build/html``.
You can serve them up locally with ``$ python -m SimpleHTTPServer``,
then view them in your web browser at ``http://127.0.0.1:8000/build/html/``.
gmusicapi-12.1.1/docs/source/index.rst 0000664 0001750 0001750 00000004446 13400371522 020151 0 ustar simon simon 0000000 0000000 gmusicapi: an unofficial API for Google Play Music
==================================================
This library allows control of
`Google Music `__ with Python.
.. code-block:: python
from gmusicapi import Mobileclient
api = Mobileclient()
# after running api.perform_oauth() once:
api.oauth_login('')
# => True
library = api.get_all_songs()
sweet_track_ids = [track['id'] for track in library
if track['artist'] == 'The Cat Empire']
playlist_id = api.create_playlist('Rad muzak')
api.add_songs_to_playlist(playlist_id, sweet_track_ids)
**This project is not supported nor endorsed by Google.**
Use common sense (protocol compliance, reasonable load, etc) and don't ruin the fun
for everyone else.
Features
--------
All major functionality is supported:
- Library management: list, create, delete, and modify songs and playlists
- Streaming and single-song downloading
- Music Manager uploading/scan-and-match and library downloading
- Most All Access features
See `the changelog
`__
for changes by version.
Using gmusicapi
---------------
.. toctree::
:hidden:
usage
ports
contributing
Getting started
+++++++++++++++
The :ref:`usage section ` has installation instructions
and some simple examples.
Api and data reference
++++++++++++++++++++++
The reference has details for all classes and functions, as well as
information on the Google Music data you'll encounter:
.. toctree::
:maxdepth: 2
reference/api
reference/protocol
Making gmusicapi better
+++++++++++++++++++++++
Contributions are always welcome!
The :ref:`contributing section ` has more details.
`The code
`__
might also be useful.
Ports and other languages
+++++++++++++++++++++++++
The :ref:`ports section ` lists known ports and information for making
ports.
Getting help
++++++++++++
Start by searching for `existing issues `__ on GitHub.
If you don't find any describing what you're seeing, go ahead and open one.
There's also ``#gmusicapi`` on Freenode, though it's pretty quiet these days.
gmusicapi-12.1.1/docs/source/ports.rst 0000664 0001750 0001750 00000003477 13374625453 020233 0 ustar simon simon 0000000 0000000 .. _ports:
Support for Other Languages
===========================
Here are the ports I'm currently aware of:
- C++: `dvirtz `__
and `Greg Wicks `__
- C#:
`ffleischer `__
and `Taylor Finnell `__
- Go: `Lari Rasku `__
- Java: `Jens Villadsen `__
and `Nick Martin `__
- Javascript:
`Lari Rasku `__.
There's also my `Google Music Turntable uploader
`__;
it's not a port, but may be useful as an example.
- Kotlin: `timtimmahh `__
- Node: `Jamon Terrell `__
- Objective-C:
`Gregory Wicks `__
- PHP:
`raydanhk `__
- Ruby: `Loic Nageleisen `__
They're in various states of completion and maintenance.
Alternatively, consider using `GMusicProxy `__ or copying its approach.
Building a Port
---------------
Get in touch if you're working on a port.
I'm happy to answer questions and point you to relevant bits of code.
Generally, though, the `protocol package
`__
is what you'll want to look at.
It contains all of the call schemas in a psuedo-dsl that's explained
`here
`__.
gmusicapi-12.1.1/docs/source/reference/ 0000755 0001750 0001750 00000000000 13512455420 020240 5 ustar simon simon 0000000 0000000 gmusicapi-12.1.1/docs/source/reference/api.rst 0000664 0001750 0001750 00000001667 13374625453 021572 0 ustar simon simon 0000000 0000000 .. _api:
.. currentmodule:: gmusicapi.clients
Client Interfaces
=================
gmusicapi currently has three main interfaces:
one for the music.google.com webclient,
one for the Android App, and
one for the Music Manager. The big differences are:
* :py:class:`Webclient` development has mostly ceased, with the :py:class:`Mobileclient`
superceding it. It is not tested nor well supported.
* :py:class:`Musicmanager` is used for uploading and downloading, while
:py:class:`Mobileclient` supports everything but uploading.
* :py:class:`Webclient`/:py:class:`Mobileclient` require a plaintext email and password to login, while
:py:class:`Musicmanager` uses `OAuth2
`__.
* :py:class:`Mobileclient` supports streaming but requires that the Google Play Music app has been installed
and run before use.
.. toctree::
:maxdepth: 2
webclient
mobileclient
musicmanager
gmusicapi-12.1.1/docs/source/reference/mobileclient.rst 0000664 0001750 0001750 00000010375 13456665572 023472 0 ustar simon simon 0000000 0000000 .. _mobileclient:
.. currentmodule:: gmusicapi.clients
Mobileclient Interface
======================
.. autoclass:: Mobileclient
Setup and login
---------------
.. automethod:: Mobileclient.__init__
.. automethod:: Mobileclient.perform_oauth
.. automethod:: Mobileclient.oauth_login
.. automethod:: Mobileclient.login
.. automethod:: Mobileclient.logout
.. automethod:: Mobileclient.is_authenticated
.. attribute:: Mobileclient.locale
The locale of the Mobileclient session used to localize some responses.
Should be an `ICU `__ locale supported by Android.
Set during login but can be changed at any time.
Account Management
------------------
.. attribute:: Mobileclient.is_subscribed
Returns the subscription status of the Google Music account.
Result is cached with a TTL of 10 minutes. To get live status before the TTL
is up, delete the ``is_subscribed`` property of the Mobileclient instance.
>>> mc = Mobileclient()
>>> mc.is_subscribed # Live status.
>>> mc.is_subscribed # Cached status.
>>> del mc.is_subscribed # Delete is_subscribed property.
>>> mc.is_subscribed # Live status.
.. automethod:: Mobileclient.get_registered_devices
.. automethod:: Mobileclient.deauthorize_device
Songs
-----
Songs are uniquely referred to within a library
with a track id in uuid format.
Store tracks also have track ids, but they are in a different
format than library track ids.
``song_id.startswith('T')`` is always ``True`` for store track ids
and ``False`` for library track ids.
Adding a store track to a library will yield a normal song id.
Store track ids can be used in most places that normal song ids can
(e.g. playlist addition or streaming).
Note that sometimes they are stored under the ``'nid'`` key, not the ``'id'`` key.
.. automethod:: Mobileclient.get_all_songs
.. automethod:: Mobileclient.get_stream_url
.. automethod:: Mobileclient.rate_songs
.. automethod:: Mobileclient.change_song_metadata
.. automethod:: Mobileclient.delete_songs
.. automethod:: Mobileclient.get_top_songs
.. automethod:: Mobileclient.increment_song_playcount
.. automethod:: Mobileclient.add_store_track
.. automethod:: Mobileclient.add_store_tracks
.. automethod:: Mobileclient.get_station_track_stream_url
Playlists
---------
Like songs, playlists have unique ids within a library.
However, their names do not need to be unique.
The tracks making up a playlist are referred to as
'playlist entries', and have unique entry ids within the
entire library (not just their containing playlist).
.. automethod:: Mobileclient.get_all_playlists
.. automethod:: Mobileclient.get_all_user_playlist_contents
.. automethod:: Mobileclient.get_shared_playlist_contents
.. automethod:: Mobileclient.create_playlist
.. automethod:: Mobileclient.delete_playlist
.. automethod:: Mobileclient.edit_playlist
.. automethod:: Mobileclient.add_songs_to_playlist
.. automethod:: Mobileclient.remove_entries_from_playlist
.. automethod:: Mobileclient.reorder_playlist_entry
Radio Stations
--------------
Radio Stations are available for free in the US only.
A subscription is required in other countries.
.. automethod:: Mobileclient.get_all_stations
.. automethod:: Mobileclient.create_station
.. automethod:: Mobileclient.delete_stations
.. automethod:: Mobileclient.get_station_tracks
Podcasts
--------
.. automethod:: Mobileclient.get_all_podcast_series
.. automethod:: Mobileclient.get_all_podcast_episodes
.. automethod:: Mobileclient.add_podcast_series
.. automethod:: Mobileclient.delete_podcast_series
.. automethod:: Mobileclient.edit_podcast_series
.. automethod:: Mobileclient.get_podcast_episode_stream_url
Search
------
Search Google Play for information about artists, albums, tracks, and more.
.. automethod:: Mobileclient.search
.. automethod:: Mobileclient.get_genres
.. automethod:: Mobileclient.get_album_info
.. automethod:: Mobileclient.get_artist_info
.. automethod:: Mobileclient.get_podcast_episode_info
.. automethod:: Mobileclient.get_podcast_series_info
.. automethod:: Mobileclient.get_track_info
.. automethod:: Mobileclient.get_station_info
Misc
----
.. automethod:: Mobileclient.get_browse_podcast_hierarchy
.. automethod:: Mobileclient.get_browse_podcast_series
.. automethod:: Mobileclient.get_listen_now_items
.. automethod:: Mobileclient.get_listen_now_situations
gmusicapi-12.1.1/docs/source/reference/musicmanager.rst 0000664 0001750 0001750 00000001143 13374625453 023461 0 ustar simon simon 0000000 0000000 .. _musicmanager:
.. currentmodule:: gmusicapi.clients
Musicmanager Interface
======================
.. autoclass:: Musicmanager
Setup and login
---------------
.. automethod:: Musicmanager.perform_oauth
.. automethod:: Musicmanager.__init__
.. automethod:: Musicmanager.login
.. automethod:: Musicmanager.logout
Uploading Songs
---------------
.. automethod:: Musicmanager.upload
Downloading Songs
-----------------
.. automethod:: Musicmanager.get_uploaded_songs
.. automethod:: Musicmanager.get_purchased_songs
.. automethod:: Musicmanager.download_song
Misc
----
..automethod:: Musicmanager.get_quota
gmusicapi-12.1.1/docs/source/reference/protocol.rst 0000664 0001750 0001750 00000002634 13400371522 022636 0 ustar simon simon 0000000 0000000 .. _protocol:
.. currentmodule:: gmusicapi.protocol
Protocol Details
================
The following definitions represent known endpoints relating to Google Music.
They are organized by the client that uses them.
The names of these classes are semantic, and may not match their actual endpoint.
Most of the time, endusers will want to use one of the :ref:`api`.
However, any of the definitions listed here can be called by using the
``_make_call`` member of a Client and providing the parameters needed by
``dynamic_*`` functions.
It's tough to generate the exact schema of every call in a readable fashion,
so this information is left out.
If you need exact specifications, look at `the code
`__
- or submit a pull request to generate the docs =)
Android Client
--------------
.. automodule:: gmusicapi.protocol.mobileclient
:members:
:undoc-members:
:exclude-members: McCall, build_request, filter_response, validate, filter_text, item_schema, genre_schema, shared_plentry
Music Manager
-------------
.. automodule:: gmusicapi.protocol.musicmanager
:members:
:undoc-members:
:exclude-members: MmCall, build_request, filter_response, validate
Web Client
----------
.. automodule:: gmusicapi.protocol.webclient
:members:
:undoc-members:
:exclude-members: WcCall, build_request, filter_response, validate, expected_response
gmusicapi-12.1.1/docs/source/reference/webclient.rst 0000664 0001750 0001750 00000002126 13374625453 022764 0 ustar simon simon 0000000 0000000 .. _webclient:
.. currentmodule:: gmusicapi.clients
.. |br| raw:: html
Webclient Interface
===================
**WARNING** |br|
**Webclient functionality is not tested nor well supported.** |br|
**Use** :class:`Mobileclient` **or** :class:`Musicmanager` **if possible.**
.. autoclass:: Webclient
Setup and login
---------------
.. automethod:: Webclient.__init__
.. automethod:: Webclient.login
.. automethod:: Webclient.logout
Song downloading and streaming
------------------------------
.. automethod:: Webclient.get_song_download_info
.. automethod:: Webclient.get_stream_audio
.. automethod:: Webclient.get_stream_urls
.. automethod:: Webclient.report_incorrect_match
Song manipulation
-----------------
.. automethod:: Webclient.upload_album_art
.. automethod:: Webclient.delete_songs
Playlist manipulation
---------------------
.. automethod:: Webclient.create_playlist
.. automethod:: Webclient.add_songs_to_playlist
.. automethod:: Webclient.remove_songs_from_playlist
.. automethod:: Webclient.get_shared_playlist_info
Other
-----
.. automethod:: Webclient.get_registered_devices
gmusicapi-12.1.1/docs/source/usage.rst 0000664 0001750 0001750 00000006232 13400371522 020141 0 ustar simon simon 0000000 0000000 .. _usage:
.. currentmodule:: gmusicapi.clients
Usage
=====
Installation
------------
Use `pip `__:
``$ pip install gmusicapi``.
To install the yet-to-be-released development version, use
``$ pip install git+https://github.com/simon-weber/gmusicapi.git@develop#egg=gmusicapi``.
If you're going to be uploading music,
you'll likely need
`avconv `__ or
`ffmpeg `__
installed and in your system path, along with at least libmp3lame:
- Linux
- Use your distro's package manager:
e.g ``$ sudo apt-get install libav-tools libavcodec-extra-53``
(ffmpeg requires extra steps on
`Debian `__/`Ubuntu `__).
- Download pre-built binaries of
`avconv `__ or `ffmpeg `__
and `edit your path `__
to include the directory that contains avconv/ffmpeg.
- Mac
- Use `Homebrew `__ to install
`libav (avconv) `__ or
`ffmpeg `__.
- Windows
- Download pre-built binaries of
`avconv `__ or `ffmpeg `__
and `edit your path `__
to include the directory that contains avconv.exe/ffmpeg.exe.
- Google App Engine
- See `this thread `__ for instructions.
The only time avconv or ffmpeg is not required is when uploading mp3s without scan-and-match enabled.
If you need to install avconv/ffmpeg from source, be sure to use
``$ ./configure --enable-gpl --enable-nonfree --enable-libmp3lame``.
Quickstart
----------
There are two supported client classes based on different Google apis.
The :py:class:`Mobileclient` uses the Android app's apis to handle
library management and playback.
The :py:class:`Musicmanager` uses the desktop Music Manager's apis to
handle uploading and downloading.
Both have similar command-line `OAuth2 `__
interfaces for logging in. For example:
.. code-block:: python
from gmusicapi import Musicmanager
mm = Musicmanager()
mm.perform_oauth()
This only needs to be run once, and if successful will save a refresh token to disk.
Then, future runs can start with:
.. code-block:: python
from gmusicapi import Musicmanager
mm = Musicmanager()
mm.login() # currently named oauth_login for the Mobileclient
If you need both library management and uploading, just create
multiple client instances.
There is also the :py:class:`Webclient`, which is uses the webapp's
apis to handle similar tasks to the Mobileclient.
It is not tested nor well supported, and requires providing full account credentials
to use. Avoid it if possible.
The reference section has complete information on all clients:
.. toctree::
:maxdepth: 2
reference/api
gmusicapi-12.1.1/example.py 0000664 0001750 0001750 00000005300 13374625453 016072 0 ustar simon simon 0000000 0000000 #!/usr/bin/env python
# -*- coding: utf-8 -*-
from __future__ import print_function, division, absolute_import, unicode_literals
from builtins import * # noqa
from getpass import getpass
from gmusicapi import Mobileclient
def ask_for_credentials():
"""Make an instance of the api and attempts to login with it.
Return the authenticated api.
"""
# We're not going to upload anything, so the Mobileclient is what we want.
api = Mobileclient()
logged_in = False
attempts = 0
while not logged_in and attempts < 3:
email = input('Email: ')
password = getpass()
logged_in = api.login(email, password, Mobileclient.FROM_MAC_ADDRESS)
attempts += 1
return api
def demonstrate():
"""Demonstrate some api features."""
api = ask_for_credentials()
if not api.is_authenticated():
print("Sorry, those credentials weren't accepted.")
return
print('Successfully logged in.')
print()
# Get all of the users songs.
# library is a big list of dictionaries, each of which contains a single song.
print('Loading library...', end=' ')
library = api.get_all_songs()
print('done.')
print(len(library), 'tracks detected.')
print()
# Show some info about a song. There is no guaranteed order;
# this is essentially a random song.
first_song = library[0]
print("The first song I see is '{}' by '{}'.".format(
first_song['title'], first_song['artist']))
# We're going to create a new playlist and add a song to it.
# Songs are uniquely identified by 'song ids', so let's get the id:
song_id = first_song['id']
print("I'm going to make a new playlist and add that song to it.")
print("I'll delete it when we're finished.")
print()
playlist_name = input('Enter a name for the playlist: ')
# Like songs, playlists have unique ids.
# Google Music allows more than one playlist of the same name;
# these ids are necessary.
playlist_id = api.create_playlist(playlist_name)
print('Made the playlist.')
print()
# Now let's add the song to the playlist, using their ids:
api.add_songs_to_playlist(playlist_id, song_id)
print('Added the song to the playlist.')
print()
# We're all done! The user can now go and see that the playlist is there.
# The web client syncs our changes in real time.
input('You can now check on Google Music that the playlist exists.\n'
'When done, press enter to delete the playlist:')
api.delete_playlist(playlist_id)
print('Deleted the playlist.')
# It's good practice to logout when finished.
api.logout()
print('All done!')
if __name__ == '__main__':
demonstrate()
gmusicapi-12.1.1/gmusicapi/ 0000755 0001750 0001750 00000000000 13512455420 016033 5 ustar simon simon 0000000 0000000 gmusicapi-12.1.1/gmusicapi/__init__.py 0000664 0001750 0001750 00000000610 13375050363 020147 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
from gmusicapi._version import __version__
from gmusicapi.clients import Webclient, Musicmanager, Mobileclient
from gmusicapi.exceptions import CallFailure
__copyright__ = 'Copyright 2018 Simon Weber'
__license__ = 'BSD 3-Clause'
__title__ = 'gmusicapi'
# appease flake8: the imports are purposeful
(__version__, Webclient, Musicmanager, Mobileclient, CallFailure)
gmusicapi-12.1.1/gmusicapi/_version.py 0000644 0001750 0001750 00000000061 13512455274 020235 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
__version__ = u"12.1.1"
gmusicapi-12.1.1/gmusicapi/appdirs.py 0000664 0001750 0001750 00000001460 13374625453 020065 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
"""
Mock version of appdirs for use in cases without the real version
"""
from __future__ import print_function, division, absolute_import, unicode_literals
from builtins import * # noqa
try:
from appdirs import AppDirs
my_appdirs = AppDirs('gmusicapi', 'Simon Weber')
except ImportError:
print('warning: could not import appdirs; will use current directory')
class FakeAppDirs(object):
to_spoof = set(base + '_dir' for base in
('user_data', 'site_data', 'user_config', 'site_config', 'user_cache',
'user_log'))
def __getattr__(self, name):
if name in self.to_spoof:
return '.' # current dir
else:
raise AttributeError
my_appdirs = FakeAppDirs()
gmusicapi-12.1.1/gmusicapi/clients/ 0000755 0001750 0001750 00000000000 13512455420 017474 5 ustar simon simon 0000000 0000000 gmusicapi-12.1.1/gmusicapi/clients/__init__.py 0000664 0001750 0001750 00000000477 13415264447 021630 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
from __future__ import print_function, division, absolute_import, unicode_literals
from gmusicapi.clients.webclient import Webclient
from gmusicapi.clients.musicmanager import Musicmanager
from gmusicapi.clients.mobileclient import Mobileclient
(Webclient, Musicmanager, Mobileclient) # noqa
gmusicapi-12.1.1/gmusicapi/clients/mobileclient.py 0000664 0001750 0001750 00000275152 13456665572 022554 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
from __future__ import print_function, division, absolute_import, unicode_literals
from past.builtins import basestring
from builtins import * # noqa
from collections import defaultdict
import datetime
from functools import partial
from operator import itemgetter
import os
import re
from uuid import getnode as getmac
from gmusicapi import session
from gmusicapi.appdirs import my_appdirs
from gmusicapi.clients.shared import _OAuthClient
from gmusicapi.exceptions import CallFailure, NotSubscribed, InvalidDeviceId
from gmusicapi.protocol import mobileclient
from gmusicapi.protocol.shared import authtypes
from gmusicapi.utils import utils
class Mobileclient(_OAuthClient):
"""Allows library management and streaming by posing as the
googleapis.com mobile clients.
Uploading is not supported by this client (use the :class:`Musicmanager`
to upload).
"""
_session_class = session.Mobileclient
_authtype = None
FROM_MAC_ADDRESS = object()
OAUTH_FILEPATH = os.path.join(my_appdirs.user_data_dir, 'mobileclient.cred')
def __init__(self, debug_logging=True, validate=True, verify_ssl=True):
super(Mobileclient, self).__init__(self.__class__.__name__,
debug_logging,
validate,
verify_ssl)
def _make_call(self, protocol, *args, **kwargs):
"""Switch the required_auth at runtime."""
if self._authtype is not None:
kwargs['required_auth'] = authtypes(**{self._authtype: True})
return super(Mobileclient, self)._make_call(protocol, *args, **kwargs)
def _ensure_device_id(self, device_id=None):
if device_id is None:
device_id = self.android_id
if len(device_id) == 16 and re.match('^[a-z0-9]*$', device_id):
# android device ids are now sent in base 10
device_id = str(int(device_id, 16))
return device_id
def _validate_device_id(self, device_id, is_mac=False):
"""Ensure that a given device_id belongs to the user supplying it."""
if is_mac: # Always allow logins with MAC address.
return device_id
device_ids = []
for d in self.get_registered_devices():
if d['id'].startswith('ios:'):
device_ids.append(d['id'])
elif d['id'].startswith('0x'):
# old android format
device_ids.append(d['id'][2:])
else:
# mac address format
device_ids.append(d['id'].replace(':', ''))
if device_id in device_ids:
return device_id
else:
self.logout()
raise InvalidDeviceId('Invalid device_id %s.' % device_id, device_ids)
@property
def locale(self):
"""The locale of the Mobileclient session used to localize some responses.
Should be an `ICU `__ locale supported by Android.
Set on authentication with :func:`login` but can be changed at any time.
"""
return self.session._locale
@locale.setter
def locale(self, locale):
self.session._locale = locale
@utils.cached_property(ttl=600)
def is_subscribed(self):
"""Returns the subscription status of the Google Music account.
Result is cached with a TTL of 10 minutes. To get live status before the TTL
is up, delete the ``is_subscribed`` property of the Mobileclient instance.
>>> mc = Mobileclient()
>>> mc.is_subscribed # Live status.
>>> mc.is_subscribed # Cached status.
>>> del mc.is_subscribed # Delete is_subscribed property.
>>> mc.is_subscribed # Live status.
"""
res = self._make_call(mobileclient.Config)
for item in res['data']['entries']:
if item['key'] == 'isNautilusUser' and item['value'] == 'true':
self.session._is_subscribed = True
break
else:
self.session._is_subscribed = False
return self.session._is_subscribed
def _login(self, session_login, android_id, locale):
if android_id is None:
raise ValueError("android_id cannot be None.")
is_mac = android_id is self.FROM_MAC_ADDRESS
if is_mac:
mac_int = getmac()
if (mac_int >> 40) % 2:
raise OSError("a valid MAC could not be determined."
" Provide an android_id (and be"
" sure to provide the same one on future runs).")
device_id = utils.create_mac_string(mac_int)
device_id = device_id.replace(':', '')
else:
device_id = android_id
if not session_login():
self.logger.info("failed to authenticate")
return False
self.android_id = self._validate_device_id(device_id, is_mac=is_mac)
self.logger.info("authenticated")
self.locale = locale
if self.is_subscribed:
self.logger.info("subscribed")
return True
def oauth_login(self, device_id, oauth_credentials=OAUTH_FILEPATH, locale='en_US'):
"""Authenticates the mobileclient with pre-existing OAuth credentials.
Returns ``True`` on success, ``False`` on failure.
:param oauth_credentials: ``oauth2client.client.OAuth2Credentials`` or the path to a
``oauth2client.file.Storage`` file. By default, the same default path used by
:func:`perform_oauth` is used.
Endusers will likely call :func:`perform_oauth` once to write
credentials to disk and then ignore this parameter.
This param
is mostly intended to allow flexibility for developers of a
3rd party service who intend to perform their own OAuth flow
(eg on their website).
:param device_id: A string of 16 hex digits for Android or "ios:" for iOS.
Alternatively, pass ``Mobileclient.FROM_MAC_ADDRESS`` to attempt to use
this machine's MAC address as an id.
If a valid MAC address cannot be determined on this machine
(which is often the case when running on a VPS), raise OSError.
This will likely be deprecated, since Google now rejects ids of this form.
:param locale: `ICU `__ locale
used to localize certain responses. This must be a locale supported
by Android. Defaults to ``'en_US'``.
"""
self._authtype = 'oauth'
session_login = partial(self._oauth_login, oauth_credentials)
return self._login(session_login, device_id, locale)
@utils.deprecated('prefer Mobileclient.oauth_login')
def login(self, email, password, android_id, locale='en_US'):
"""Authenticates the Mobileclient using full account credentials.
Returns ``True`` on success, ``False`` on failure.
Behind the scenes, this performs a Google-specific Android login flow
with the provided credentials, then trades those for a Google Music auth token.
It is deprecated in favor of the more robust :func:`oauth_login`,
which performs a normal OAuth flow instead of taking account credentials.
:param email: eg ``'test@gmail.com'`` or just ``'test'``.
:param password: password or app-specific password for 2-factor users.
This is not stored locally, and is sent securely over SSL.
:param android_id: 16 hex digits, eg ``'1234567890abcdef'``.
Pass Mobileclient.FROM_MAC_ADDRESS instead to attempt to use
this machine's MAC address as an android id.
**Use this at your own risk**:
the id will be a non-standard 12 characters,
but appears to work fine in testing.
If a valid MAC address cannot be determined on this machine
(which is often the case when running on a VPS), raise OSError.
:param locale: `ICU `__ locale
used to localize certain responses. This must be a locale supported
by Android. Defaults to ``'en_US'``.
"""
self._authtype = 'gpsoauth'
session_login = partial(self.session.gpsoauth_login, email, password, android_id)
return self._login(session_login, android_id, locale)
# TODO expose max/page-results, etc for list operations
def get_all_songs(self, incremental=False, include_deleted=None, updated_after=None):
"""Returns a list of dictionaries that each represent a song.
:param incremental: if True, return a generator that yields lists
of at most 1000 tracks
as they are retrieved from the server. This can be useful for
presenting a loading bar to a user.
:param include_deleted: ignored. Will be removed in a future release.
:param updated_after: a datetime.datetime; defaults to unix epoch.
If provided, deleted songs may be returned.
Here is an example song dictionary::
{
'comment':'',
'rating':'0',
'albumArtRef':[
{
'url': 'http://lh6.ggpht.com/...'
}
],
'artistId':[
'Aod62yyj3u3xsjtooghh2glwsdi'
],
'composer':'',
'year':2011,
'creationTimestamp':'1330879409467830',
'id':'5924d75a-931c-30ed-8790-f7fce8943c85',
'album':'Heritage ',
'totalDiscCount':0,
'title':'Haxprocess',
'recentTimestamp':'1372040508935000',
'albumArtist':'',
'trackNumber':6,
'discNumber':0,
'deleted':False,
'storeId':'Txsffypukmmeg3iwl3w5a5s3vzy',
'nid':'Txsffypukmmeg3iwl3w5a5s3vzy',
'totalTrackCount':10,
'estimatedSize':'17229205',
'albumId':'Bdkf6ywxmrhflvtasnayxlkgpcm',
'beatsPerMinute':0,
'genre':'Progressive Metal',
'playCount':7,
'artistArtRef':[
{
'url': 'http://lh3.ggpht.com/...'
}
],
'kind':'sj#track',
'artist':'Opeth',
'lastModifiedTimestamp':'1330881158830924',
'clientId':'+eGFGTbiyMktbPuvB5MfsA',
'durationMillis':'418000'
}
"""
tracks = self._get_all_items(mobileclient.ListTracks, incremental,
updated_after=updated_after)
return tracks
@utils.accept_singleton(dict)
@utils.empty_arg_shortcircuit
def rate_songs(self, songs, rating):
"""Rate library or store songs.
Returns rated song ids.
:param songs: a list of song dictionaries
or a single song dictionary.
required keys: 'id' for library songs or 'nid' and 'trackType' for store songs.
:param rating: set to ``'0'`` (no thumb), ``'1'`` (down thumb), or ``'5'`` (up thumb).
"""
mutate_call = mobileclient.BatchMutateTracks
mutations = []
for song in songs:
song['rating'] = rating
mutations.append({'update': song})
self._make_call(mutate_call, mutations)
# TODO
# store tracks don't send back their id, so we're
# forced to spoof this
return [utils.id_or_nid(song) for song in songs]
@utils.accept_singleton(dict)
@utils.empty_arg_shortcircuit
@utils.deprecated('prefer Mobileclient.rate_songs')
def change_song_metadata(self, songs):
"""Changes the metadata of tracks.
Returns a list of the song ids changed.
:param songs: a list of song dictionaries
or a single song dictionary.
Currently, only the ``rating`` key can be changed.
Set it to ``'0'`` (no thumb), ``'1'`` (down thumb), or ``'5'`` (up thumb).
You can also use this to rate store tracks
that aren't in your library, eg::
song = mc.get_track_info('')
song['rating'] = '5'
mc.change_song_metadata(song)
"""
mutate_call = mobileclient.BatchMutateTracks
mutations = [{'update': s} for s in songs]
self._make_call(mutate_call, mutations)
# TODO
# store tracks don't send back their id, so we're
# forced to spoof this
return [utils.id_or_nid(d) for d in songs]
def increment_song_playcount(self, song_id, plays=1, playtime=None):
"""Increments a song's playcount and returns its song id.
:params song_id: a song id. Providing the id of a store track
that has been added to the library will *not* increment the
corresponding library song's playcount. To do this, use the
'id' field (which looks like a uuid and doesn't begin with 'T'),
not the 'nid' field.
:params plays: (optional) positive number of plays to increment by.
The default is 1.
:params playtime: (optional) a datetime.datetime of the
time the song was played.
It will default to the time of the call.
"""
if playtime is None:
playtime = datetime.datetime.now()
self._make_call(mobileclient.IncrementPlayCount, song_id, plays, playtime)
return song_id
@utils.require_subscription
@utils.enforce_id_param
@utils.deprecated('prefer Mobileclient.add_store_tracks')
def add_store_track(self, store_song_id):
"""Adds a store track to the library
Returns the library track id of added store track.
:param store_song_id: store song id
"""
return self.add_store_tracks(store_song_id)[0]
@utils.require_subscription
@utils.accept_singleton(basestring)
@utils.enforce_ids_param
def add_store_tracks(self, store_song_ids):
"""Add store tracks to the library
Returns a list of the library track ids of added store tracks.
:param store_song_ids: a list of store song ids or a single store song id
"""
mutate_call = mobileclient.BatchMutateTracks
add_mutations = [mutate_call.build_track_add(self.get_track_info(store_song_id))
for store_song_id in store_song_ids]
res = self._make_call(mutate_call, add_mutations)
return [r['id'] for r in res['mutate_response']]
@utils.accept_singleton(basestring)
@utils.enforce_ids_param
@utils.empty_arg_shortcircuit
def delete_songs(self, library_song_ids):
"""Deletes songs from the library.
Returns a list of deleted song ids.
:param song_ids: a list of song ids, or a single song id.
"""
mutate_call = mobileclient.BatchMutateTracks
del_mutations = mutate_call.build_track_deletes(library_song_ids)
res = self._make_call(mutate_call, del_mutations)
return [d['id'] for d in res['mutate_response']]
@utils.enforce_id_param
def get_stream_url(self, song_id, device_id=None, quality='hi'):
"""Returns a url that will point to an mp3 file.
:param song_id: A single song id.
This can be ``'storeId'`` from a store song, ``'id'`` from an uploaded song, or
``'trackId'`` from a playlist entry.
:param device_id: (optional) defaults to ``android_id`` from login.
Otherwise, provide a mobile device id as a string.
Android device ids are 16 characters, while iOS ids
are uuids with 'ios:' prepended.
If you have already used Google Music on a mobile device,
:func:`Mobileclient.get_registered_devices
` will provide
at least one working id. Omit ``'0x'`` from the start of the string if present.
Registered computer ids (a MAC address) will not be accepted and will 403.
Providing an unregistered mobile device id will register it to your account,
subject to Google's `device limits
`__.
**Registering a device id that you do not own is likely a violation of the TOS.**
:param quality: (optional) stream bits per second quality
One of three possible values, hi: 320kbps, med: 160kbps, low: 128kbps.
The default is hi
When handling the resulting url, keep in mind that:
* you will likely need to handle redirects
* the url expires after a minute
* only one IP can be streaming music at once.
This can result in an http 403 with
``X-Rejected-Reason: ANOTHER_STREAM_BEING_PLAYED``.
The file will not contain metadata.
Use :func:`Webclient.get_song_download_info
`
or :func:`Musicmanager.download_song
`
to download files with metadata.
"""
if song_id.startswith('T') and not self.is_subscribed:
raise NotSubscribed("Store tracks require a subscription to stream.")
device_id = self._ensure_device_id(device_id)
return self._make_call(mobileclient.GetStreamUrl, song_id, device_id, quality)
def get_station_track_stream_url(self, song_id, wentry_id, session_token, quality='hi'):
"""Returns a url that will point to an mp3 file.
This is only for use by free accounts, and requires a call to
:func:`get_station_info` first to provide `wentry_id` and `session_token`.
Subscribers should instead use :func:`get_stream_url`.
:param song_id: a single song id
:param wentry_id: a free radio station track entry id (`wentryid` from
:func:`get_station_info`)
:param session_token: a free radio station session token (`sessionToken` from
:func:`get_station_info`)
:param quality: (optional) stream bits per second quality
One of three possible values, hi: 320kbps, med: 160kbps, low: 128kbps.
The default is hi
"""
return self._make_call(mobileclient.GetStationTrackStreamUrl, song_id, wentry_id,
session_token, quality)
def get_all_playlists(self, incremental=False, include_deleted=None, updated_after=None):
"""Returns a list of dictionaries that each represent a playlist.
:param incremental: if True, return a generator that yields lists
of at most 1000 playlists
as they are retrieved from the server. This can be useful for
presenting a loading bar to a user.
:param include_deleted: ignored. Will be removed in a future release.
:param updated_after: a datetime.datetime; defaults to unix epoch
If provided, deleted playlists may be returned.
Here is an example playlist dictionary::
{
# can also be SHARED (public/subscribed to), MAGIC or omitted
'type': 'USER_GENERATED',
'kind': 'sj#playlist',
'name': 'Something Mix',
'deleted': False,
'lastModifiedTimestamp': '1325458766483033',
'recentTimestamp': '1325458766479000',
'shareToken': '',
'ownerProfilePhotoUrl': 'http://lh3.googleusercontent.com/...',
'ownerName': 'Simon Weber',
'accessControlled': False, # has to do with shared playlists
'creationTimestamp': '1325285553626172',
'id': '3d72c9b5-baad-4ff7-815d-cdef717e5d61'
}
"""
playlists = self._get_all_items(mobileclient.ListPlaylists, incremental,
updated_after=updated_after)
return playlists
# these could trivially support multiple creation/edits/deletion, but
# I chose to match the old webclient interface (at least for now).
def create_playlist(self, name, description=None, public=False):
"""Creates a new empty playlist and returns its id.
:param name: the desired title.
Creating multiple playlists with the same name is allowed.
:param description: (optional) the desired description
:param public: (optional) if True and the user has a subscription, share playlist.
"""
share_state = 'PUBLIC' if public else 'PRIVATE'
mutate_call = mobileclient.BatchMutatePlaylists
add_mutations = mutate_call.build_playlist_adds([{'name': name,
'description': description,
'public': share_state}])
res = self._make_call(mutate_call, add_mutations)
return res['mutate_response'][0]['id']
@utils.enforce_id_param
def edit_playlist(self, playlist_id, new_name=None, new_description=None, public=None):
"""Changes the name of a playlist and returns its id.
:param playlist_id: the id of the playlist
:param new_name: (optional) desired title
:param new_description: (optional) desired description
:param public: (optional) if True and the user has a subscription, share playlist.
"""
if all(value is None for value in (new_name, new_description, public)):
raise ValueError('new_name, new_description, or public must be provided')
if public is None:
share_state = public
else:
share_state = 'PUBLIC' if public else 'PRIVATE'
mutate_call = mobileclient.BatchMutatePlaylists
update_mutations = mutate_call.build_playlist_updates([
{'id': playlist_id, 'name': new_name,
'description': new_description, 'public': share_state}
])
res = self._make_call(mutate_call, update_mutations)
return res['mutate_response'][0]['id']
@utils.enforce_id_param
def delete_playlist(self, playlist_id):
"""Deletes a playlist and returns its id.
:param playlist_id: the id to delete.
"""
# TODO accept multiple?
mutate_call = mobileclient.BatchMutatePlaylists
del_mutations = mutate_call.build_playlist_deletes([playlist_id])
res = self._make_call(mutate_call, del_mutations)
return res['mutate_response'][0]['id']
def get_all_user_playlist_contents(self):
"""
Retrieves the contents of *all* user-created playlists
-- the Mobileclient does not support retrieving
only the contents of one
playlist.
This will not return results for public playlists
that the user is subscribed to; use :func:`get_shared_playlist_contents`
instead.
The same structure as :func:`get_all_playlists`
will be returned, but
with the addition of a ``'tracks'`` key in each dict
set to a list of properly-ordered playlist entry dicts.
Here is an example playlist entry for an individual track::
{
'kind': 'sj#playlistEntry',
'deleted': False,
'trackId': '2bb0ab1c-ce1a-3c0f-9217-a06da207b7a7',
'lastModifiedTimestamp': '1325285553655027',
'playlistId': '3d72c9b5-baad-4ff7-815d-cdef717e5d61',
'absolutePosition': '01729382256910287871', # denotes playlist ordering
'source': '1', # '2' if hosted on Google Music, '1' otherwise (see below)
'creationTimestamp': '1325285553655027',
'id': 'c9f1aff5-f93d-4b98-b13a-429cc7972fea' ## see below
}
If a user uploads local music to Google Music using the Music Manager,
Google will attempt to match each uploaded track to a track already
hosted on its servers. If a match is found for a track, the playlist
entry key ``'source'`` has the value ``'2'``, and the entry will have a
key ``'track'`` with a value that is a dict of track metadata (title,
artist, etc).
If a track is not hosted on Google Music, then the playlist entry key
``'source'`` has the value ``'1'``, and may not have a ``'track'``
key (e.g., for an MP3 without ID3 tags). In this case, the key ``'trackId'``
corresponds to the column ``ServerId`` in the table ``XFILES`` in Music
Manager's local SQLite database (stored, e.g., at
~/Library/Application\ Support/Google/MusicManager/ServerDatabase.db
on OS X). Among other things, the SQLite database exposes the track's
local file path, and Music Manager's imputed metadata.
(Note that the above behavior is documented for the Music Manager set to
sync from local Folders, and may differ if it instead syncs from iTunes.)
"""
user_playlists = [p for p in self.get_all_playlists()
if (p.get('type') == 'USER_GENERATED' or
p.get('type') != 'SHARED' or
'type' not in p)]
all_entries = self._get_all_items(mobileclient.ListPlaylistEntries,
incremental=False,
updated_after=None)
for playlist in user_playlists:
# TODO could use a dict to make this faster
entries = [e for e in all_entries
if e['playlistId'] == playlist['id']]
entries.sort(key=itemgetter('absolutePosition'))
playlist['tracks'] = entries
return user_playlists
def get_shared_playlist_contents(self, share_token):
"""
Retrieves the contents of a public playlist.
:param share_token: from ``playlist['shareToken']``, or a playlist share
url (``https://play.google.com/music/playlist/``).
Note that tokens from urls will need to be url-decoded,
eg ``AM...%3D%3D`` becomes ``AM...==``.
For example, to retrieve the contents of a playlist that the user is
subscribed to::
subscribed_to = [p for p in mc.get_all_playlists() if p.get('type') == 'SHARED']
share_tok = subscribed_to[0]['shareToken']
tracks = mc.get_shared_playlist_contents(share_tok)
The user need not be subscribed to a playlist to list its tracks.
Returns a list of playlist entries
with structure the same as those
returned by :func:`get_all_user_playlist_contents`,
but without the ``'clientId'`` or ``'playlistId'`` keys.
"""
res = self._make_call(mobileclient.ListSharedPlaylistEntries,
updated_after=None, share_token=share_token)
entries = res['entries'][0]['playlistEntry']
entries.sort(key=itemgetter('absolutePosition'))
return entries
@utils.accept_singleton(basestring, 2)
@utils.enforce_id_param
@utils.enforce_ids_param(position=2)
@utils.empty_arg_shortcircuit(position=2)
def add_songs_to_playlist(self, playlist_id, song_ids):
"""Appends songs to the end of a playlist.
Returns a list of playlist entry ids that were added.
:param playlist_id: the id of the playlist to add to.
:param song_ids: a list of song ids, or a single song id.
These can be ``'storeId'`` from a store song, ``'id'`` from an uploaded song, or
``'trackId'`` from a playlist entry.
Playlists have a maximum size of 1000 songs.
Calls may fail before that point (presumably) due to
an error on Google's end (see `#239
`__).
"""
mutate_call = mobileclient.BatchMutatePlaylistEntries
add_mutations = mutate_call.build_plentry_adds(playlist_id, song_ids)
res = self._make_call(mutate_call, add_mutations)
return [e['id'] for e in res['mutate_response']]
@utils.accept_singleton(basestring, 1)
@utils.enforce_ids_param(position=1)
@utils.empty_arg_shortcircuit(position=1)
def remove_entries_from_playlist(self, entry_ids):
"""Removes specific entries from a playlist.
Returns a list of entry ids that were removed.
:param entry_ids: a list of entry ids, or a single entry id.
"""
mutate_call = mobileclient.BatchMutatePlaylistEntries
del_mutations = mutate_call.build_plentry_deletes(entry_ids)
res = self._make_call(mutate_call, del_mutations)
return [e['id'] for e in res['mutate_response']]
def reorder_playlist_entry(self, entry, to_follow_entry=None, to_precede_entry=None):
"""Reorders a single entry in a playlist and returns its id.
Read ``reorder_playlist_entry(foo, bar, gaz)`` as
"reorder playlist entry *foo* to follow entry *bar*
and precede entry *gaz*."
:param entry: the playlist entry to move.
:param to_follow_entry: the playlist entry
that will come before *entry* in the resulting playlist,
or None if *entry* is to be the first entry in the playlist.
:param to_precede_entry: the playlist entry
that will come after *entry* in the resulting playlist
or None if *entry* is to be the last entry in the playlist.
``reorder_playlist_entry(foo)`` is invalid and will raise ValueError;
provide at least one of *to_follow_entry* or *to_precede_entry*.
Leaving *to_follow_entry* or *to_precede_entry* as None when
*entry* is not to be the first or last entry in the playlist
is undefined.
All params are dicts returned by
:func:`get_all_user_playlist_contents` or
:func:`get_shared_playlist_contents`.
"""
if to_follow_entry is None and to_precede_entry is None:
raise ValueError('either to_follow_entry or to_precede_entry must be provided')
mutate_call = mobileclient.BatchMutatePlaylistEntries
before = to_follow_entry['clientId'] if to_follow_entry else None
after = to_precede_entry['clientId'] if to_precede_entry else None
reorder_mutation = mutate_call.build_plentry_reorder(entry, before, after)
res = self._make_call(mutate_call, [reorder_mutation])
return [e['id'] for e in res['mutate_response']]
# WIP, see issue #179
# def reorder_playlist(self, reordered_playlist, orig_playlist=None):
# """TODO"""
# if not reordered_playlist['tracks']:
# #TODO what to return?
# return
# if orig_playlist is None:
# #TODO get pl from server
# pass
# if len(reordered_playlist['tracks']) != len(orig_playlist['tracks']):
# raise ValueError('the original playlist does not have the same number of'
# ' tracks as the reordered playlist')
# # find the minimum number of mutations to match the orig playlist
# orig_tracks = orig_playlist['tracks']
# orig_tracks_id_to_idx = dict([(t['id'], i) for (i, t) in enumerate(orig_tracks)])
# re_tracks = reordered_playlist['tracks']
# re_tracks_id_to_idx = dict([(t['id'], i) for (i, t) in enumerate(re_tracks)])
# translated_re_tracks = [orig_tracks_id_to_idx[t['id']] for t in re_tracks]
# lis = utils.longest_increasing_subseq(translated_re_tracks)
# idx_to_move = set(range(len(orig_tracks))) - set(lis)
# idx_pos_pairs = [(i, re_tracks_id_to_idx[orig_tracks[i]['id']])
# for i in idx_to_move]
# #TODO build out mutations
# return idx_pos_pairs
# @staticmethod
# def _create_ple_reorder_mutations(tracks, from_to_idx_pairs):
# """
# Return a list of mutations.
# :param tracks: orig_playlist['tracks']
# :param from_to_idx_pairs: [(from_index, to_index)]
# """
# for from_idx, to_idx in sorted(key=itemgetter(1)
# playlist_len = len(self.plentry_ids)
# for from_pos, to_pos in [pair for pair in
# itertools.product(range(playlist_len), repeat=2)
# if pair[0] < pair[1]]:
# pl = self.mc_get_playlist_songs(self.playlist_id)
# from_e = pl[from_pos]
# e_before_new_pos, e_after_new_pos = None, None
# if to_pos - 1 >= 0:
# e_before_new_pos = pl[to_pos]
# if to_pos + 1 < playlist_len:
# e_after_new_pos = pl[to_pos + 1]
# self.mc.reorder_playlist_entry(from_e,
# to_follow_entry=e_before_new_pos,
# to_precede_entry=e_after_new_pos)
# self._mc_assert_ple_position(from_e, to_pos)
# if e_before_new_pos:
# self._mc_assert_ple_position(e_before_new_pos, to_pos - 1)
# if e_after_new_pos:
# self._mc_assert_ple_position(e_after_new_pos, to_pos + 1)
def get_registered_devices(self):
"""
Returns a list of dictionaries representing devices associated with the account.
Performing the :class:`Musicmanager` OAuth flow will register a device
of type ``'DESKTOP_APP'``.
Installing the Android or iOS Google Music app and logging into it will
register a device of type ``'ANDROID'`` or ``'IOS'`` respectively, which is
required for streaming with the :class:`Mobileclient`.
Here is an example response::
[
{
u'kind': u'sj#devicemanagementinfo',
u'friendlyName': u'my-hostname',
u'id': u'AA:BB:CC:11:22:33',
u'lastAccessedTimeMs': u'1394138679694',
u'type': u'DESKTOP_APP'
},
{
u"kind": u"sj#devicemanagementinfo",
u'friendlyName': u'Nexus 7',
u'id': u'0x00112233aabbccdd', # remove 0x when streaming
u'lastAccessedTimeMs': u'1344808742774',
u'type': u'ANDROID'
u'smartPhone': True
},
{
u"kind": u"sj#devicemanagementinfo",
u'friendlyName': u'iPhone 6',
u'id': u'ios:01234567-0123-0123-0123-0123456789AB',
u'lastAccessedTimeMs': 1394138679694,
u'type': u'IOS'
u'smartPhone': True
}
{
u'kind': u'sj#devicemanagementinfo',
u'friendlyName': u'Google Play Music for Chrome on Windows',
u'id': u'rt2qfkh0qjhos4bxrgc0oae...', # 64 characters, alphanumeric
u'lastAccessedTimeMs': u'1425602805052',
u'type': u'DESKTOP_APP'
},
]
"""
res = self._make_call(mobileclient.GetDeviceManagementInfo)
return res['data']['items'] if 'data' in res else []
def deauthorize_device(self, device_id):
"""Deauthorize a registered device.
Returns ``True`` on success, ``False`` on failure.
:param device_id: A mobile device id as a string.
Android ids are 16 characters with '0x' prepended,
iOS ids are uuids with 'ios:' prepended,
while desktop ids are in the form of a MAC address.
Providing an invalid or unregistered device id will result in a 400 HTTP error.
Google limits the number of device deauthorizations to 4 per year.
Attempts to deauthorize a device when that limit is reached results in
a 403 HTTP error with: ``X-Rejected-Reason: TOO_MANY_DEAUTHORIZATIONS``.
"""
try:
self._make_call(mobileclient.DeauthDevice, device_id)
except CallFailure:
self.logger.exception("Deauthorization failure.")
return False
return True
def get_top_songs(self):
"""Returns a list of dictionaries that each represent a track.
Only store tracks will be returned.
Tops songs seem to be an auto-generated playlist of
positively-rated songs (Thumbs up).
See :func:`get_track_info` for the format of a track dictionary.
"""
return self._get_all_items(mobileclient.ListPromotedTracks,
incremental=False,
updated_after=None)
@utils.deprecated('prefer Mobileclient.get_top_songs')
def get_promoted_songs(self):
"""Returns a list of dictionaries that each represent a track.
Only store tracks will be returned.
Promoted tracks are determined in an unknown fashion,
but positively-rated library tracks are common.
It is deprecated in favor of the more clearly scoped
:func:`get_tops_songs`.
See :func:`get_track_info` for the format of a track dictionary.
"""
return self.get_top_songs()
def get_listen_now_items(self):
"""Returns a list of dictionaries of Listen Now albums and stations.
See :func:`get_listen_now_situations` for Listen Now situations.
Here is an example Listen Now album::
{
'album': {
'artist_metajam_id': 'A2mfgoustq7iqjdbvlenw7pnap4',
'artist_name': 'Justin Bieber',
'artist_profile_image': {
'url': 'http://lh3.googleusercontent.com/XgktDR74DWE9xD...'',
},
'description': 'Purpose is the fourth studio album by Canadian...',
'description_attribution': {
'kind': 'sj#attribution',
'license_title': 'Creative Commons Attribution CC-BY-SA 4.0',
'license_url': 'http://creativecommons.org/licenses/by-sa/4.0/legalcode',
'source_title': 'Wikipedia',
'source_url': 'http://en.wikipedia.org/wiki/Purpose_(Justin_Bieber_album)',
},
'id': {
'artist': 'Justin Bieber',
'metajamCompactKey': 'Bqpez5cimsze2fh6w7j2rcf55xa',
'title': 'Purpose (Deluxe)',
},
'title': 'Purpose (Deluxe)'
'images': [
{
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/m66cbl4Jl3VNz...',
},
],
}
'kind': 'sj#listennowitem',
'suggestion_reason': '9',
'suggestion_text': 'Popular album on Google Play Music',
'type': '1'
}
Here is an example Listen Now station::
{
'radio_station': {
'id': {
'seeds': [
{
'artistId': 'Ax6ociylvowozcz2iepfqsar54i',
'kind': 'sj#radioSeed',
'metadataSeed': {
'artist': {
'artistArtRef': 'http://lh3.googleusercontent.com/x9qukAx...',
'artistArtRefs': [
{
'aspectRatio': '2',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/x9qukAx...',
},
],
'artistId': 'Ax6ociylvowozcz2iepfqsar54i',
'artist_bio_attribution': {
'kind': 'sj#attribution',
'source_title': 'artist representative',
},
'kind': 'sj#artist',
'name': 'Drake',
},
'kind': 'sj#radioSeedMetadata',
},
'seedType': '3',
},
]
},
'title': 'Drake',
},
'compositeArtRefs': [
{
'aspectRatio': '2',
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/rE39ky1yZN...',
},
{
'aspectRatio': '1',
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/Pcwg_HngBr...',
},
],
'images': [
{
'aspectRatio': '2',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/x9qukAx_TMam...',
},
],
'suggestion_reason': '9',
'suggestion_text': 'Popular artist on Google Play Music',
'type': '3'
}
"""
res = self._make_call(mobileclient.ListListenNowItems)
return res['listennow_items']
def get_listen_now_situations(self):
"""Returns a list of dictionaries that each represent a Listen Now situation.
See :func:`get_listen_now_items` for Listen Now albums and stations.
A situation contains a list of related stations or other situations.
Here is an example situation::
{
'description': 'Select a station of today's most popular songs.',
'id': 'Ntiiwllegkw73p27o236mfsj674',
'imageUrl': 'http://lh3.googleusercontent.com/egm4NgIK-Cmh84GjVgH...',
'stations': [
{
'compositeArtRefs': [
{
'aspectRatio': '2',
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/ffDI377y...',
},
],
'contentTypes': ['1'],
'description': "This playlist features today's biggest pop songs...",
'imageUrls': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/B4iKX23Z...',
},
],
'kind': 'sj#radioStation',
'name': "Today's Pop Hits",
'seed': {
'curatedStationId': 'Lgen6kdn43tz5b3edimqd5e4ckq',
'kind': 'sj#radioSeed',
'seedType': '9',
},
'skipEventHistory': [],
'stationSeeds': [
{
'curatedStationId': 'Lgen6kdn43tz5b3edimqd5e4ckq',
'kind': 'sj#radioSeed',
'seedType': '9',
},
],
}
],
'title': "Today's Biggest Hits",
'wideImageUrl': 'http://lh3.googleusercontent.com/13W-bm3sNmSfOjUkEqY...'
}
"""
return self._make_call(mobileclient.ListListenNowSituations)['situations']
def get_browse_podcast_hierarchy(self):
"""Retrieve the hierarchy of podcast browse genres.
Returns a list of podcast genres and subgenres::
{
"groups": [
{
"id": "JZCpodcasttopchart",
"displayName": "Top Charts",
"subgroups": [
{
"id": "JZCpodcasttopchartall",
"displayName": "All categories"
},
{
"id": "JZCpodcasttopchartarts",
"displayName": "Arts"
},
{
"id": "JZCpodcasttopchartbusiness",
"displayName": "Business"
},
{
"id": "JZCpodcasttopchartcomedy",
"displayName": "Comedy"
},
{
"id": "JZCpodcasttopcharteducation",
"displayName": "Education"
},
{
"id": "JZCpodcasttopchartgames",
"displayName": "Games & hobbies"
},
{
"id": "JZCpodcasttopchartgovernment",
"displayName": "Government & organizations"
},
{
"id": "JZCpodcasttopcharthealth",
"displayName": "Health"
},
{
"id": "JZCpodcasttopchartkids",
"displayName": "Kids & families"
},
{
"id": "JZCpodcasttopchartmusic",
"displayName": "Music"
},
{
"id": "JZCpodcasttopchartnews",
"displayName": "News & politics"
},
{
"id": "JZCpodcasttopchartreligion",
"displayName": "Religion & spirituality"
},
{
"id": "JZCpodcasttopchartscience",
"displayName": "Science & medicine"
},
{
"id": "JZCpodcasttopchartsociety",
"displayName": "Society & culture"
},
{
"id": "JZCpodcasttopchartsports",
"displayName": "Sports & recreation"
},
{
"id": "JZCpodcasttopcharttechnology",
"displayName": "Technology"
},
{
"id": "JZCpodcasttopcharttv",
"displayName": "TV & film"
}
]
}
]
}
"""
res = self._make_call(mobileclient.GetBrowsePodcastHierarchy)
return res.get('groups', [])
def get_browse_podcast_series(self, genre_id='JZCpodcasttopchartall'):
"""Retrieve podcast series from browse podcasts by genre.
:param genre_id: A podcast genre id as returned by :func:`get_podcast_browse_hierarchy`.
Defaults to Top Chart 'All categories'.
Returns a list of podcast series dicts.
Here is an example podcast series dict::
{
'art': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/liR-Pm7EhB58wrAa4uo9Y33LcJJ8keU...'
}
],
'author': 'NBC Sports Radio',
'continuationToken': '',
'description': 'Mike Florio talks about the biggest NFL topics with the '
'people who are most passionate about the game: League execs, '
'players, coaches and the journalists who cover pro football.',
'explicitType': '2',
'link': 'https://audioboom.com/channel/pro-football-talk-live-with-mike-florio',
'seriesId': 'I3iad5heqorm3nck6yp7giruc5i',
'title': 'Pro Football Talk Live with Mike Florio',
'totalNumEpisodes': 0
}
"""
res = self._make_call(mobileclient.ListBrowsePodcastSeries, id=genre_id)
return res.get('series', [])
def get_all_podcast_series(self, device_id=None, incremental=False,
include_deleted=None, updated_after=None):
"""Retrieve list of user-subscribed podcast series.
:param device_id: (optional) defaults to ``android_id`` from login.
Otherwise, provide a mobile device id as a string.
Android device ids are 16 characters, while iOS ids
are uuids with 'ios:' prepended.
If you have already used Google Music on a mobile device,
:func:`Mobileclient.get_registered_devices
` will provide
at least one working id. Omit ``'0x'`` from the start of the string if present.
Registered computer ids (a MAC address) will not be accepted and will 403.
Providing an unregistered mobile device id will register it to your account,
subject to Google's `device limits
`__.
**Registering a device id that you do not own is likely a violation of the TOS.**
:param incremental: if True, return a generator that yields lists
of at most 1000 podcast series
as they are retrieved from the server. This can be useful for
presenting a loading bar to a user.
:param include_deleted: ignored. Will be removed in a future release.
:param updated_after: a datetime.datetime; defaults to unix epoch
Returns a list of podcast series dicts.
Here is an example podcast series dict::
{
'art': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/bNoyxoGTwCGkUscMjHsvKe5W80uMOfq...'
}
],
'author': 'Chris Hardwick',
'continuationToken': '',
'description': 'I am Chris Hardwick. I am on TV a lot and have a blog at '
'nerdist.com. This podcast is basically just me talking about '
'stuff and things with my two nerdy friends Jonah Ray and Matt '
'Mira, and usually someone more famous than all of us. '
'Occasionally we swear because that is fun. I hope you like '
"it, but if you don't I'm sure you will not hesitate to unfurl "
"your rage in the 'reviews' section because that's how the "
'Internet works.',
'explicitType': '1',
'link': 'http://nerdist.com/',
'seriesId': 'Iliyrhelw74vdqrro77kq2vrdhy',
'title': 'The Nerdist',
'totalNumEpisodes': 829,
'userPreferences': {
'autoDownload': False,
'notifyOnNewEpisode': False,
'subscribed': True
}
}
"""
device_id = self._ensure_device_id(device_id)
return self._get_all_items(mobileclient.ListPodcastSeries, incremental=incremental,
updated_after=updated_after,
device_id=device_id)
def get_all_podcast_episodes(self, device_id=None, incremental=False,
include_deleted=None, updated_after=None):
"""Retrieve list of episodes from user-subscribed podcast series.
:param device_id: (optional) defaults to ``android_id`` from login.
Otherwise, provide a mobile device id as a string.
Android device ids are 16 characters, while iOS ids
are uuids with 'ios:' prepended.
If you have already used Google Music on a mobile device,
:func:`Mobileclient.get_registered_devices
` will provide
at least one working id. Omit ``'0x'`` from the start of the string if present.
Registered computer ids (a MAC address) will not be accepted and will 403.
Providing an unregistered mobile device id will register it to your account,
subject to Google's `device limits
`__.
**Registering a device id that you do not own is likely a violation of the TOS.**
:param incremental: if True, return a generator that yields lists
of at most 1000 podcast episodes
as they are retrieved from the server. This can be useful for
presenting a loading bar to a user.
:param include_deleted: ignored. Will be removed in a future release.
:param updated_after: a datetime.datetime; defaults to unix epoch
Returns a list of podcast episode dicts.
Here is an example podcast episode dict::
{
'art': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/bNoyxoGTwCGkUscMjHsvKe5W80uMOfq...'
}
],
'deleted': False,
'description': 'Comedian Bill Burr yelled at Philadelphia, Chris vaguely '
'understands hockey, Jonah understands it even less, and Matt '
'is weirdly not tired of the running "Matt loves the Dave '
'Matthews Band" joke, though I\'m sure all of you are.',
'durationMillis': '4310000',
'episodeId': 'D6i26frpxu53t2ws3lpbjtpovum',
'explicitType': '2',
'fileSize': '69064793',
'publicationTimestampMillis': '1277791500000',
'seriesId': 'Iliyrhelw74vdqrro77kq2vrdhy',
'seriesTitle': 'The Nerdist',
'title': 'Bill Burr'
}
"""
device_id = self._ensure_device_id(device_id)
return self._get_all_items(mobileclient.ListPodcastEpisodes, incremental=incremental,
updated_after=updated_after,
device_id=device_id)
# TODO: Support multiple.
@utils.enforce_id_param
def add_podcast_series(self, podcast_id, notify_on_new_episode=False):
"""Subscribe to a podcast series.
:param podcast_id: A podcast series id (hint: they always start with 'I').
:param notify_on_new_episode: Get device notifications on new episodes.
Returns podcast series id of added podcast series
"""
mutate_call = mobileclient.BatchMutatePodcastSeries
update_mutations = mutate_call.build_podcast_updates([
{
'seriesId': podcast_id,
'subscribed': True,
'userPreferences': {
'subscribed': True,
'notifyOnNewEpisode': notify_on_new_episode
}
}
])
res = self._make_call(mutate_call, update_mutations)
return res['mutate_response'][0]['id']
# TODO: Support multiple.
@utils.enforce_id_param
def delete_podcast_series(self, podcast_id):
"""Unsubscribe to a podcast series.
:param podcast_id: A podcast series id (hint: they always start with 'I').
Returns podcast series id of removed podcast series
"""
mutate_call = mobileclient.BatchMutatePodcastSeries
update_mutations = mutate_call.build_podcast_updates([
{
'seriesId': podcast_id,
'subscribed': False,
'userPreferences': {
'subscribed': False,
'notifyOnNewEpisode': False
}
}
])
res = self._make_call(mutate_call, update_mutations)
return res['mutate_response'][0]['id']
# TODO: Support multiple.
@utils.enforce_id_param
def edit_podcast_series(self, podcast_id, subscribe=True, notify_on_new_episode=False):
"""Edit a podcast series subscription.
:param podcast_id: A podcast series id (hint: they always start with 'I').
:param subscribe: Subscribe to podcast.
:param notify_on_new_episode: Get device notifications on new episodes.
Returns podcast series id of edited podcast series
"""
mutate_call = mobileclient.BatchMutatePodcastSeries
update_mutations = mutate_call.build_podcast_updates([
{
'seriesId': podcast_id,
'subscribed': subscribe,
'userPreferences': {
'subscribed': subscribe,
'notifyOnNewEpisode': notify_on_new_episode
}
}
])
res = self._make_call(mutate_call, update_mutations)
return res['mutate_response'][0]['id']
@utils.enforce_id_param
def get_podcast_episode_stream_url(self, podcast_episode_id, device_id=None, quality='hi'):
"""Returns a url that will point to an mp3 file.
:param podcast_episde_id: a single podcast episode id (hint: they always start with 'D').
:param device_id: (optional) defaults to ``android_id`` from login.
Otherwise, provide a mobile device id as a string.
Android device ids are 16 characters, while iOS ids
are uuids with 'ios:' prepended.
If you have already used Google Music on a mobile device,
:func:`Mobileclient.get_registered_devices
` will provide
at least one working id. Omit ``'0x'`` from the start of the string if present.
Registered computer ids (a MAC address) will not be accepted and will 403.
Providing an unregistered mobile device id will register it to your account,
subject to Google's `device limits
`__.
**Registering a device id that you do not own is likely a violation of the TOS.**
:param quality: (optional) stream bits per second quality
One of three possible values, hi: 320kbps, med: 160kbps, low: 128kbps.
The default is hi
When handling the resulting url, keep in mind that:
* you will likely need to handle redirects
* the url expires after a minute
* only one IP can be streaming music at once.
This can result in an http 403 with
``X-Rejected-Reason: ANOTHER_STREAM_BEING_PLAYED``.
The file will not contain metadata.
"""
device_id = self._ensure_device_id(device_id)
return self._make_call(
mobileclient.GetPodcastEpisodeStreamUrl, podcast_episode_id, device_id, quality)
def get_podcast_series_info(self, podcast_series_id, max_episodes=50):
"""Retrieves information about a podcast series.
:param podcast_series_id: A podcast series id (hint: they always start with 'I').
:param max_episodes: Maximum number of episodes to retrieve
Returns a dict, eg::
{
'art': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/bNoyxoGTwCGkUscMjHsvKe5W80uMOfq...'
}
],
'author': 'Chris Hardwick',
'continuationToken': '',
'description': 'I am Chris Hardwick. I am on TV a lot and have a blog at '
'nerdist.com. This podcast is basically just me talking about '
'stuff and things with my two nerdy friends Jonah Ray and Matt '
'Mira, and usually someone more famous than all of us. '
'Occasionally we swear because that is fun. I hope you like '
"it, but if you don't I'm sure you will not hesitate to unfurl "
"your rage in the 'reviews' section because that's how the "
'Internet works.',
'episodes': [
{
'art': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/bNoyxoGTwCGkUscMjHsvKe5...'
}
],
'description': 'Sarah Jessica Parker (Sex and the City) '
'chats with Chris about growing up without '
'television, her time on Square Pegs and '
'her character in L.A. Story. Sarah Jessica '
'then talks about how she felt when she first '
'got the part of Carrie on Sex and the '
'City, how she dealt with her sudden '
'celebrity of being Carrie Bradshaw and they '
'come up with a crazy theory about the show! '
'They also talk about Sarah Jessica’s new '
'show Divorce on HBO!',
'durationMillis': '5101000',
'episodeId': 'Dcz67vtkhrerzh4hptfqpadt5vm',
'explicitType': '1',
'fileSize': '40995252',
'publicationTimestampMillis': '1475640000000',
'seriesId': 'Iliyrhelw74vdqrro77kq2vrdhy',
'seriesTitle': 'The Nerdist',
'title': 'Sarah Jessica Parker'
},
]
'explicitType': '1',
'link': 'http://nerdist.com/',
'seriesId': 'Iliyrhelw74vdqrro77kq2vrdhy',
'title': 'The Nerdist',
'totalNumEpisodes': 829,
'userPreferences': {
'autoDownload': False,
'notifyOnNewEpisode': False,
'subscribed': True
}
}
"""
return self._make_call(mobileclient.GetPodcastSeries, podcast_series_id, max_episodes)
def get_podcast_episode_info(self, podcast_episode_id):
"""Retrieves information about a podcast episode.
:param podcast_episode_id: A podcast episode id (hint: they always start with 'D').
Returns a dict, eg::
{
'art': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/bNoyxoGTwCGkUscMjHsvKe5...'
}
],
'description': 'Sarah Jessica Parker (Sex and the City) '
'chats with Chris about growing up without '
'television, her time on Square Pegs and '
'her character in L.A. Story. Sarah Jessica '
'then talks about how she felt when she first '
'got the part of Carrie on Sex and the '
'City, how she dealt with her sudden '
'celebrity of being Carrie Bradshaw and they '
'come up with a crazy theory about the show! '
'They also talk about Sarah Jessica’s new '
'show Divorce on HBO!',
'durationMillis': '5101000',
'episodeId': 'Dcz67vtkhrerzh4hptfqpadt5vm',
'explicitType': '1',
'fileSize': '40995252',
'publicationTimestampMillis': '1475640000000',
'seriesId': 'Iliyrhelw74vdqrro77kq2vrdhy',
'seriesTitle': 'The Nerdist',
'title': 'Sarah Jessica Parker'
}
"""
return self._make_call(mobileclient.GetPodcastEpisode, podcast_episode_id)
def create_station(self, name,
track_id=None, artist_id=None, album_id=None,
genre_id=None, playlist_token=None, curated_station_id=None):
"""Creates a radio station and returns its id.
:param name: the name of the station to create
:param \*_id: the id of an item to seed the station from.
:param playlist_token: The shareToken of a playlist to seed the station from.
Exactly one of the id/token params must be provided, or ValueError
will be raised.
"""
# TODO could expose include_tracks
seed = {}
if track_id is not None:
if track_id[0] == 'T':
seed['trackId'] = track_id
seed['seedType'] = 2
else:
seed['trackLockerId'] = track_id
seed['seedType'] = 1
if artist_id is not None:
seed['artistId'] = artist_id
seed['seedType'] = 3
if album_id is not None:
seed['albumId'] = album_id
seed['seedType'] = 4
if genre_id is not None:
seed['genreId'] = genre_id
seed['seedType'] = 5
if playlist_token is not None:
seed['playlistShareToken'] = playlist_token
seed['seedType'] = 8
if curated_station_id is not None:
seed['curatedStationId'] = curated_station_id
seed['seedType'] = 9
if len(seed) > 2:
raise ValueError('exactly one {track,artist,album,genre}_id must be provided')
mutate_call = mobileclient.BatchMutateStations
add_mutation = mutate_call.build_add(name, seed, include_tracks=False, num_tracks=0)
res = self._make_call(mutate_call, [add_mutation])
return res['mutate_response'][0]['id']
@utils.accept_singleton(basestring)
@utils.enforce_ids_param
@utils.empty_arg_shortcircuit
def delete_stations(self, station_ids):
"""Deletes radio stations and returns their ids.
:param station_ids: a single id, or a list of ids to delete
"""
mutate_call = mobileclient.BatchMutateStations
delete_mutations = mutate_call.build_deletes(station_ids)
res = self._make_call(mutate_call, delete_mutations)
return [s['id'] for s in res['mutate_response']]
def get_all_stations(self, incremental=False, include_deleted=None, updated_after=None):
"""Retrieve all library stations.
Returns a list of dictionaries that each represent a radio station.
This includes any stations listened to recently, which might not be in the library.
:param incremental: if True, return a generator that yields lists
of at most 1000 stations
as they are retrieved from the server. This can be useful for
presenting a loading bar to a user.
:param include_deleted: ignored. Will be removed in a future release.
:param updated_after: a datetime.datetime; defaults to unix epoch
Here is an example station dictionary::
{
'imageUrl': 'http://lh6.ggpht.com/...',
'kind': 'sj#radioStation',
'name': 'station',
'deleted': False,
'lastModifiedTimestamp': '1370796487455005',
'recentTimestamp': '1370796487454000',
'clientId': 'c2639bf4-af24-4e4f-ab37-855fc89d15a1',
'seed':
{
'kind': 'sj#radioSeed',
'trackLockerId': '7df3aadd-9a18-3dc1-b92e-a7cf7619da7e'
# possible keys:
# albumId, artistId, genreId, trackId, trackLockerId
},
'id': '69f1bfce-308a-313e-9ed2-e50abe33a25d',
'inLibrary': True
},
"""
return self._get_all_items(mobileclient.ListStations, incremental,
updated_after=updated_after)
def get_station_tracks(self, station_id, num_tracks=25, recently_played_ids=None):
"""Returns a list of dictionaries that each represent a track.
Each call performs a separate sampling (with replacement?)
from all possible tracks for the station.
Nonexistent stations will return an empty list.
:param station_id: the id of a radio station to retrieve tracks from.
Use the special id ``'IFL'`` for the "I'm Feeling Lucky" station.
:param num_tracks: the number of tracks to retrieve
:param recently_played_ids: a list of recently played track
ids retrieved from this station. This avoids playing
duplicates.
See :func:`get_all_songs` for the format of a track dictionary.
"""
if recently_played_ids is None:
recently_played_ids = []
def add_track_type(track_id):
if track_id[0] == 'T':
return {'id': track_id, 'type': 1}
else:
return {'id': track_id, 'type': 0}
recently_played = [add_track_type(track_id) for track_id in recently_played_ids]
res = self._make_call(mobileclient.ListStationTracks,
station_id, num_tracks, recently_played=recently_played)
stations = res.get('data', {}).get('stations')
if not stations:
return []
return stations[0].get('tracks', [])
def search(self, query, max_results=100):
"""Queries Google Music for content.
Most result types will be empty unless using a subscription account.
:param query: a string keyword to search with. Capitalization and punctuation are ignored.
:param max_results: Maximum number of items to be retrieved.
The maximum accepted value is 100. If set higher, results are limited to 10.
A value of ``None`` allows up to 1000 results per type but
won't return playlist nor situation results.
Default is ``100``.
The results are returned as a dictionary of lists mapped by result type:
* album_hits
* artist_hits
* genre_hits
* playlist_hits
* podcast_hits
* situation_hits
* song_hits
* station_hits
* video_hits
Here is a sample of results for a search of ``'workout'`` on a subscription account::
{
'album_hits': [{
'album': {
'albumArtRef': 'http://lh5.ggpht.com/DVIg4GiD6msHfgPs_Vu_2eRxCyAoz0fF...',
'albumArtist': 'J.Cole',
'albumId': 'Bfp2tuhynyqppnp6zennhmf6w3y',
'artist': 'J.Cole',
'artistId': ['Ajgnxme45wcqqv44vykrleifpji'],
'description_attribution': {
'kind': 'sj#attribution',
'license_title': 'Creative Commons Attribution CC-BY',
'license_url': 'http://creativecommons.org/licenses/by/4.0/legalcode',
'source_title': 'Freebase',
'source_url': ''
},
'explicitType': '1',
'kind': 'sj#album',
'name': 'Work Out',
'year': 2011
},
'type': '3'
}],
'artist_hits': [{
'artist': {
'artistArtRef': 'http://lh3.googleusercontent.com/MJe-cDw9uQ-pUagoLlm...',
'artistArtRefs': [{
'aspectRatio': '2',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/MJe-cDw9uQ-pUagoLlmKX3x_K...'
}],
'artistId': 'Ajgnxme45wcqqv44vykrleifpji',
'artist_bio_attribution': {
'kind': 'sj#attribution',
'source_title': 'David Jeffries, Rovi'
},
'kind': 'sj#artist',
'name': 'J. Cole'
},
'type': '2'
}],
'playlist_hits': [{
'playlist': {
'albumArtRef': [
{'url': 'http://lh3.googleusercontent.com/KJsAhrg8Jk_5A4xYLA68LFC...'}
],
'description': 'Workout Plan ',
'kind': 'sj#playlist',
'name': 'Workout',
'ownerName': 'Ida Sarver',
'shareToken': 'AMaBXyktyF6Yy_G-8wQy8Rru0tkueIbIFblt2h0BpkvTzHDz-fFj6P...',
'type': 'SHARED'
},
'type': '4'
}],
'podcast_hits': [{
'series': {
'art': [
{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'https://lh3.googleusercontent.com/je4lsaiQCdfcOWoYm3Z_mC...'
}
],
'author': 'Steve Boyett',
'continuationToken': '',
'copyright': 'Music copyright c the respective artists. All other '
'material c2006, 2016 by Podrunner, LLC. All rights '
'reserved. For personal use only. Unauthorized '
'reproduction, sale, rental, exchange, public '
'performance, or broadcast of this audio is '
'prohibited.',
'description': 'Nonstop, one-hour, high-energy workout music mixes '
"to help you groove while you move. Podrunner's "
'fixed-tempo and interval exercise mixes are '
'perfect for power walking, jogging, running, '
'spinning, elliptical, aerobics, and many other '
'tempo-based forms of exercise. An iTunes '
'award-winner six years in a row!',
'explicitType': '2',
'link': 'http://www.podrunner.com/',
'seriesId': 'Ilx4ufdua5rdvzplnojtloulo3a',
'title': 'PODRUNNER: Workout Music',
'totalNumEpisodes': 0
},
'type': '9'
}],
'situation_hits': [{
'situation': {
'description':
'Level up and enter beast mode with some loud, aggressive music.',
'id': 'Nrklpcyfewwrmodvtds5qlfp5ve',
'imageUrl': 'http://lh3.googleusercontent.com/Cd8WRMaG_pDwjTC_dSPIIuf...',
'title': 'Entering Beast Mode',
'wideImageUrl': 'http://lh3.googleusercontent.com/8A9S-nTb5pfJLcpS8P...'},
'type': '7'
}],
'song_hits': [{
'track': {
'album': 'Work Out',
'albumArtRef': [{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh5.ggpht.com/DVIg4GiD6msHfgPs_Vu_2eRxCyAoz0fFdxj5w...'
}],
'albumArtist': 'J.Cole',
'albumAvailableForPurchase': True,
'albumId': 'Bfp2tuhynyqppnp6zennhmf6w3y',
'artist': 'J Cole',
'artistId': ['Ajgnxme45wcqqv44vykrleifpji', 'Ampniqsqcwxk7btbgh5ycujij5i'],
'composer': '',
'discNumber': 1,
'durationMillis': '234000',
'estimatedSize': '9368582',
'explicitType': '1',
'genre': 'Pop',
'kind': 'sj#track',
'nid': 'Tq3nsmzeumhilpegkimjcnbr6aq',
'primaryVideo': {
'id': '6PN78PS_QsM',
'kind': 'sj#video',
'thumbnails': [{
'height': 180,
'url': 'https://i.ytimg.com/vi/6PN78PS_QsM/mqdefault.jpg',
'width': 320
}]
},
'storeId': 'Tq3nsmzeumhilpegkimjcnbr6aq',
'title': 'Work Out',
'trackAvailableForPurchase': True,
'trackAvailableForSubscription': True,
'trackNumber': 1,
'trackType': '7',
'year': 2011
},
'type': '1'
}],
'station_hits': [{
'station': {
'compositeArtRefs': [{
'aspectRatio': '1',
'kind': 'sj#imageRef',
'url': 'http://lh3.googleusercontent.com/3aD9mFppy6PwjADnjwv_w...'
}],
'contentTypes': ['1'],
'description':
'These riff-tastic metal tracks are perfect '
'for getting the blood pumping.',
'imageUrls': [{
'aspectRatio': '1',
'autogen': False,
'kind': 'sj#imageRef',
'url': 'http://lh5.ggpht.com/YNGkFdrtk43e8H941fuAHjflrNZ1CJUeqdoys...'
}],
'kind': 'sj#radioStation',
'name': 'Heavy Metal Workout',
'seed': {
'curatedStationId': 'Lcwg73w3bd64hsrgarnorif52r',
'kind': 'sj#radioSeed',
'seedType': '9'
},
'skipEventHistory': [],
'stationSeeds': [{
'curatedStationId': 'Lcwg73w3bd64hsrgarnorif52r',
'kind': 'sj#radioSeed',
'seedType': '9'}
]},
'type': '6'
}],
'video_hits': [{
'score': 629.6226806640625,
'type': '8',
'youtube_video': {
'id': '6PN78PS_QsM',
'kind': 'sj#video',
'thumbnails': [{
'height': 180,
'url': 'https://i.ytimg.com/vi/6PN78PS_QsM/mqdefault.jpg',
'width': 320
}],
'title': 'J. Cole - Work Out'
}
}]
}
"""
res = self._make_call(mobileclient.Search, query, max_results)
clusters = res.get('clusterDetail', [])
hits_by_type = defaultdict(list)
for cluster in clusters:
hit_type = cluster['cluster']['type']
hits = cluster.get('entries', [])
hits_by_type[hit_type].extend(hits)
return {'album_hits': hits_by_type['3'],
'artist_hits': hits_by_type['2'],
'playlist_hits': hits_by_type['4'],
'genre_hits': hits_by_type['5'],
'podcast_hits': hits_by_type['9'],
'situation_hits': hits_by_type['7'],
'song_hits': hits_by_type['1'],
'station_hits': hits_by_type['6'],
'video_hits': hits_by_type['8']}
@utils.enforce_id_param
def get_artist_info(self, artist_id, include_albums=True, max_top_tracks=5, max_rel_artist=5):
"""Retrieves details on an artist.
:param artist_id: an artist id (hint: they always start with 'A')
:param include_albums: when True, create the ``'albums'`` substructure
:param max_top_tracks: maximum number of top tracks to retrieve
:param max_rel_artist: maximum number of related artists to retrieve
Returns a dict, eg::
{
'albums':[ # only if include_albums is True
{
'albumArtRef':'http://lh6.ggpht.com/...',
'albumArtist':'Amorphis',
'albumId':'Bfr2onjv7g7tm4rzosewnnwxxyy',
'artist':'Amorphis',
'artistId':[
'Apoecs6off3y6k4h5nvqqos4b5e'
],
'kind':'sj#album',
'name':'Circle',
'year':2013
},
],
'artistArtRef': 'http://lh6.ggpht.com/...',
'artistId':'Apoecs6off3y6k4h5nvqqos4b5e',
'kind':'sj#artist',
'name':'Amorphis',
'related_artists':[ # only if max_rel_artists > 0
{
'artistArtRef': 'http://lh5.ggpht.com/...',
'artistId':'Aheqc7kveljtq7rptd7cy5gvk2q',
'kind':'sj#artist',
'name':'Dark Tranquillity'
}
],
'topTracks':[ # only if max_top_tracks > 0
{
'album':'Skyforger',
'albumArtRef':[
{
'url': 'http://lh4.ggpht.com/...'
}
],
'albumArtist':'Amorphis',
'albumAvailableForPurchase':True,
'albumId':'B5nc22xlcmdwi3zn5htkohstg44',
'artist':'Amorphis',
'artistId':[
'Apoecs6off3y6k4h5nvqqos4b5e'
],
'discNumber':1,
'durationMillis':'253000',
'estimatedSize':'10137633',
'kind':'sj#track',
'nid':'Tn2ugrgkeinrrb2a4ji7khungoy',
'playCount':1,
'storeId':'Tn2ugrgkeinrrb2a4ji7khungoy',
'title':'Silver Bride',
'trackAvailableForPurchase':True,
'trackNumber':2,
'trackType':'7'
}
],
'total_albums':21
}
"""
res = self._make_call(mobileclient.GetArtist,
artist_id, include_albums, max_top_tracks, max_rel_artist)
return res
def _get_all_items(self, call, incremental, **kwargs):
"""
:param call: protocol.McCall
:param incremental: bool
kwargs are passed to the call.
"""
if not incremental:
# slight optimization: get more items in a page
kwargs.setdefault('max_results', 20000)
generator = self._get_all_items_incremental(call, **kwargs)
if incremental:
return generator
return [s for chunk in generator for s in chunk]
def _get_all_items_incremental(self, call, **kwargs):
"""Return a generator of lists of tracks.
kwargs are passed to the call."""
get_next_chunk = True
lib_chunk = {}
next_page_token = None
while get_next_chunk:
lib_chunk = self._make_call(call,
start_token=next_page_token,
**kwargs)
items = []
for item in lib_chunk['data']['items']:
if 'userPreferences' in item:
if item['userPreferences'].get('subscribed', False):
items.append(item)
elif ('updated_after' in kwargs) or (not item.get('deleted', False)):
items.append(item)
# Conditional prevents generator from yielding empty
# list for last page of podcast list calls.
if items:
yield items
# Podcast list calls always include 'nextPageToken' in responses.
# We have to check to make sure we don't get stuck in an infinite loop
# by comparing the previous and next page tokens.
prev_page_token = next_page_token
next_page_token = lib_chunk.get('nextPageToken')
get_next_chunk = (next_page_token and next_page_token != prev_page_token)
@utils.enforce_id_param
def get_album_info(self, album_id, include_tracks=True):
"""Retrieves details on an album.
:param album_id: an album id (hint: they always start with 'B')
:param include_tracks: when True, create the ``'tracks'`` substructure
Returns a dict, eg::
{
'kind': 'sj#album',
'name': 'Circle',
'artist': 'Amorphis',
'albumArtRef': 'http://lh6.ggpht.com/...',
'tracks': [ # if `include_tracks` is True
{
'album': 'Circle',
'kind': 'sj#track',
'storeId': 'T5zb7luo2vkroozmj57g2nljdsy', # can be used as a song id
'artist': 'Amorphis',
'albumArtRef': [
{
'url': 'http://lh6.ggpht.com/...'
}],
'title': 'Shades of Grey',
'nid': 'T5zb7luo2vkroozmj57g2nljdsy',
'estimatedSize': '13115591',
'albumId': 'Bfr2onjv7g7tm4rzosewnnwxxyy',
'artistId': ['Apoecs6off3y6k4h5nvqqos4b5e'],
'albumArtist': 'Amorphis',
'durationMillis': '327000',
'composer': '',
'genre': 'Metal',
'trackNumber': 1,
'discNumber': 1,
'trackAvailableForPurchase': True,
'trackType': '7',
'albumAvailableForPurchase': True
}, # ...
],
'albumId': 'Bfr2onjv7g7tm4rzosewnnwxxyy',
'artistId': ['Apoecs6off3y6k4h5nvqqos4b5e'],
'albumArtist': 'Amorphis',
'year': 2013
}
"""
return self._make_call(mobileclient.GetAlbum, album_id, include_tracks)
@utils.enforce_id_param
def get_track_info(self, store_track_id):
"""Retrieves information about a store track.
:param store_track_id: a store track id (hint: they always start with 'T')
Returns a dict, eg::
{
'album': 'Best Of',
'kind': 'sj#track',
'storeId': 'Te2qokfjmhqxw4bnkswbfphzs4m',
'artist': 'Amorphis',
'albumArtRef': [
{
'url': 'http://lh5.ggpht.com/...'
}],
'title': 'Hopeless Days',
'nid': 'Te2qokfjmhqxw4bnkswbfphzs4m',
'estimatedSize': '12325643',
'albumId': 'Bsbjjc24a5xutbutvbvg3h4y2k4',
'artistId': ['Apoecs6off3y6k4h5nvqqos4b5e'],
'albumArtist': 'Amorphis',
'durationMillis': '308000',
'composer': '',
'genre': 'Metal',
'trackNumber': 2,
'discNumber': 1,
'trackAvailableForPurchase': True,
'trackType': '7',
'albumAvailableForPurchase': True
}
"""
return self._make_call(mobileclient.GetStoreTrack, store_track_id)
@utils.enforce_id_param
def get_station_info(self, station_id, num_tracks=25):
"""Retrieves information about a station.
:param station_id: a station id
:param include_tracks: when True, create the ``'tracks'`` substructure
:param num_tracks: maximum number of tracks to return
Returns a dict, eg::
{
'kind':'sj#radioStation',
'byline':'By Google Play Music',
'name':'Neo Soul',
'compositeArtRefs':[
{
'url':'http://lh3.googleusercontent.com/Aa-WBVTbKegp6McwqeHlj6KX5EYHKOBag74uwNl4xIHSv1g7Mi-NkMzwig',
'kind':'sj#imageRef',
'aspectRatio':'2'
} ],
'deleted':False,
'enforcementResult':{
'sessionInvalidated':False
},
'lastModifiedTimestamp':'1497410517701000',
'recentTimestamp':'1497410516945000',
'clientId':'9e66e89e-50b0-11e7-aaa3-bc5ff4545c4b',
'sessionToken':'AFTSR9PtB_PbyqZ3jsnl-PFma4upK1MEtlhVnIlxRNynGalctoJF4TpgzaxymOnk0Gv5DQG7gb_W3eLamPU_Mg1cWylhrowQi1EFMBKWHeDDYWzpU1cEOF-D3c_gnwsBRHIuOetph2veY2Fd-dKVzjOkN6mtidE-XPR2VnpR9PG83wRLVRtJq5593-Vvbu6wjCHD9f23ohxg-ki0tyD3fjFW1463zy63YzN5Aa2SpbvOskEWhwhS3u9ASgEoX08lePE-ZZAq1XtmVvLa8DnDMVb7i95Qhp0dM2it1uruKHH85u7tMYnttbAW4022d0rqrp3ULDKOYMvIIouXH44-bkbKLuVIADiqeNavwTVzcoJxWo4mMKjCaxM=',
'tracks':[
{
'albumArtRef':[
{
'url':'http://lh5.ggpht.com/vWRj9DkKZ7cFj-qXoGoBGsv7ngUWdtGNl1SSOdzj2efDwdAs3F0kJ3Xq6zLxKjgv1v3ive5S',
'kind':'sj#imageRef',
'aspectRatio':'1',
'autogen':False
}
],
'artistId':[
'Atmjrctnubes5zhftrey2xjkzl'
],
'composer':'',
'year':1996,
'trackAvailableForSubscription':True,
'trackType':'7',
'album':u"Maxwell's Urban Hang Suite",
'title':u"Ascension (Don't Ever Wonder)",
'albumArtist':'Maxwell',
'trackNumber':4,
'discNumber':1,
'albumAvailableForPurchase':False,
'explicitType':'2',
'trackAvailableForPurchase':True,
'storeId':'T6utayayrlyfmpovgj4ulacpat',
'nid':'T6utayayrlyfmpovgj4ulacpat',
'estimatedSize':'13848059',
'albumId':'Bpwzztxynfjwtnrtgiugem3b56e',
'genre':'Neo-Soul',
'kind':'sj#track',
'primaryVideo':{
'kind':'sj#video',
'id':'D7rm9t5S4uE',
'thumbnails':[
{
'url':'https://i.ytimg.com/vi/D7rm9t5S4uE/mqdefault.jpg',
'width':320,
'height':180
}
]
},
'artist':'Maxwell',
'wentryid':'ec9428eb-2676-4e92-901d-2de9a72fe581',
'durationMillis':'346000'
}
],
'seed':{
'kind':'sj#radioSeed',
'curatedStationId':'L3lu7bpcqtd3e7pa7w37rf7gdu',
'seedType':'9'
},
'skipEventHistory':[
],
'inLibrary':False,
'imageUrls':[
{
'url':'http://lh3.googleusercontent.com/iceDDsQjQ683AD4w21WWlekg115Ixy_kMTivkFJTjo3w7vuW4-SSs3F3KQOaR8qoI-QYVuOQoA',
'kind':'sj#imageRef',
'aspectRatio':'1',
'autogen':False
}
],
'id':'1a9ec96c-6c98-3c43-b123-9e2743203f5d'
}
"""
res = self._make_call(mobileclient.ListStationTracks, station_id, num_tracks, [])
return res.get('data', {'stations': [{}]})['stations'][0]
def get_genres(self, parent_genre_id=None):
"""Retrieves information on Google Music genres.
:param parent_genre_id: (optional) If provided, only child genres
will be returned. By default, all root genres are returned.
If this id is invalid, an empty list will be returned.
Returns a list of dicts of the form, eg::
{
'name': 'Alternative/Indie',
'id': 'ALTERNATIVE_INDIE'
'kind': 'sj#musicGenre',
'children': [ # this key may not be present
'ALTERNATIVE_80S', # these are ids
'ALT_COUNTRY',
# ...
],
'images': [
{
# these are album covers representative of the genre
'url': 'http://lh6.ggpht.com/...'
},
# ...
],
}
Note that the id can be used with :func:`create_station`
to seed a radio station.
"""
res = self._make_call(mobileclient.GetGenres, parent_genre_id)
# An invalid parent genre won't respond with a genres key.
return res.get('genres', [])
gmusicapi-12.1.1/gmusicapi/clients/musicmanager.py 0000664 0001750 0001750 00000056021 13400371522 022523 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
from __future__ import print_function, division, absolute_import, unicode_literals
from future.utils import PY3
from past.builtins import basestring
from builtins import * # noqa
import os
from socket import gethostname
import time
from uuid import getnode as getmac
if PY3:
from urllib.parse import unquote
else:
from urllib import unquote
import httplib2 # included with oauth2client
from oauth2client.client import TokenRevokeError
import gmusicapi
from gmusicapi.clients.shared import _OAuthClient
from gmusicapi.appdirs import my_appdirs
from gmusicapi.exceptions import CallFailure, NotLoggedIn
from gmusicapi.protocol import musicmanager, upload_pb2, locker_pb2
from gmusicapi.utils import utils
from gmusicapi import session
class Musicmanager(_OAuthClient):
"""Allows uploading by posing as Google's Music Manager.
Musicmanager uses OAuth, so a plaintext email and password are not required
when logging in.
For most authors and users of gmusicapi scripts,
:func:`perform_oauth` should be run once per machine to
store credentials to disk.
Future calls to :func:`login` can use
use the stored credentials by default.
Some authors may want more control over the OAuth flow.
In this case, credentials can be directly provided to :func:`login`.
"""
OAUTH_FILEPATH = os.path.join(my_appdirs.user_data_dir, 'oauth.cred')
_session_class = session.Musicmanager
def __init__(self, debug_logging=True, validate=True, verify_ssl=True):
super(Musicmanager, self).__init__(self.__class__.__name__,
debug_logging,
validate,
verify_ssl)
def login(self, oauth_credentials=OAUTH_FILEPATH,
uploader_id=None, uploader_name=None):
"""Authenticates the Music Manager using OAuth.
Returns ``True`` on success, ``False`` on failure.
Unlike the :class:`Webclient`, OAuth allows authentication without
providing plaintext credentials to the application.
In most cases, the default parameters should be acceptable. Users on
virtual machines will want to provide `uploader_id`.
:param oauth_credentials: ``oauth2client.client.OAuth2Credentials`` or the path to a
``oauth2client.file.Storage`` file. By default, the same default path used by
:func:`perform_oauth` is used.
Endusers will likely call :func:`perform_oauth` once to write
credentials to disk and then ignore this parameter.
This param
is mostly intended to allow flexibility for developers of a
3rd party service who intend to perform their own OAuth flow
(eg on their website).
:param uploader_id: a unique id as a MAC address, eg ``'00:11:22:33:AA:BB'``.
This should only be provided in cases where the default
(host MAC address incremented by 1) will not work.
Upload behavior is undefined if a Music Manager uses the same id, especially when
reporting bad matches.
``ValueError`` will be raised if this is provided but not in the proper form.
``OSError`` will be raised if this is not provided and a real MAC could not be
determined (most common when running on a VPS).
If provided, use the same id on all future runs for this machine,
because of the upload device limit explained below.
:param uploader_name: human-readable non-unique id; default is
``" (gmusicapi-{version})"``.
This doesn't appear to be a part of authentication at all.
Registering with (id, name = X, Y) and logging in with
(id, name = X, Z) works, and does not change the server-stored
uploader_name.
There are hard limits on how many upload devices can be registered; refer to `Google's
docs `__. There
have been limits on deauthorizing devices in the past, so it's smart not to register
more devices than necessary.
"""
return (self._oauth_login(oauth_credentials) and
self._perform_upauth(uploader_id, uploader_name))
def _perform_upauth(self, uploader_id, uploader_name):
"""Auth or register ourselves as an upload client.
Return True on success; see :py:func:`login` for params.
"""
if uploader_id is None:
mac_int = getmac()
if (mac_int >> 40) % 2:
self.session.logout()
raise OSError('a valid MAC could not be determined.'
' Provide uploader_id (and be'
' sure to provide the same one on future runs).')
else:
# distinguish us from a Music Manager on this machine
mac_int = (mac_int + 1) % (1 << 48)
uploader_id = utils.create_mac_string(mac_int)
if not utils.is_valid_mac(uploader_id):
self.session.logout()
raise ValueError('uploader_id is not in a valid form.'
'\nProvide 6 pairs of hex digits'
' with capital letters',
' (eg "00:11:22:33:AA:BB")')
if uploader_name is None:
uploader_name = gethostname() + u" (gmusicapi-%s)" % gmusicapi.__version__
try:
# this is a MM-specific step that might register a new device.
self._make_call(musicmanager.AuthenticateUploader,
uploader_id,
uploader_name)
self.logger.info("successful upauth")
self.uploader_id = uploader_id
self.uploader_name = uploader_name
except CallFailure:
self.logger.exception("upauth failure")
self.session.logout()
return False
return True
def logout(self, revoke_oauth=False):
"""Forgets local authentication in this Client instance.
:param revoke_oauth: if True, oauth credentials will be permanently
revoked. If credentials came from a file, it will be deleted.
Returns ``True`` on success."""
# TODO the login/logout stuff is all over the place
success = True
if revoke_oauth:
try:
# this automatically deletes a Storage file, if present
self.session._oauth_creds.revoke(httplib2.Http())
except TokenRevokeError:
self.logger.exception("could not revoke oauth credentials")
success = False
self.uploader_id = None
self.uploader_name = None
return success and super(Musicmanager, self).logout()
# mostly copy-paste from Webclient.get_all_songs.
# not worried about overlap in this case; the logic of either could change.
def get_uploaded_songs(self, incremental=False):
"""Returns a list of dictionaries, each with the following keys:
``('id', 'title', 'album', 'album_artist', 'artist', 'track_number',
'track_size', 'disc_number', 'total_disc_count')``.
All Access tracks that were added to the library will not be included,
only tracks uploaded/matched by the user.
:param incremental: if True, return a generator that yields lists
of at most 1000 dictionaries
as they are retrieved from the server. This can be useful for
presenting a loading bar to a user.
"""
to_return = self._get_all_songs()
if not incremental:
to_return = [song for chunk in to_return for song in chunk]
return to_return
# mostly copy-paste from Webclient.get_all_songs.
# not worried about overlap in this case; the logic of either could change.
def get_purchased_songs(self, incremental=False):
"""Returns a list of dictionaries, each with the following keys:
``('id', 'title', 'album', 'album_artist', 'artist', 'track_number',
'track_size', 'disc_number', 'total_disc_count')``.
:param incremental: if True, return a generator that yields lists
of at most 1000 dictionaries
as they are retrieved from the server. This can be useful for
presenting a loading bar to a user.
"""
to_return = self._get_all_songs(export_type=2)
if not incremental:
to_return = [song for chunk in to_return for song in chunk]
return to_return
@staticmethod
def _track_info_to_dict(track_info):
"""Given a download_pb2.DownloadTrackInfo, return a dictionary."""
# figure it's better to hardcode keys here than use introspection
# and risk returning a new field all of a sudden.
return dict((field, getattr(track_info, field)) for field in
('id', 'title', 'album', 'album_artist', 'artist',
'track_number', 'track_size', 'disc_number',
'total_disc_count'))
def _get_all_songs(self, export_type=1):
"""Return a generator of song chunks."""
get_next_chunk = True
# need to spoof .continuation_token access, and
# can't add attrs to object(). Can with functions.
lib_chunk = lambda: 0 # noqa
lib_chunk.continuation_token = None
while get_next_chunk:
lib_chunk = self._make_call(musicmanager.ListTracks,
self.uploader_id,
lib_chunk.continuation_token,
export_type)
yield [self._track_info_to_dict(info)
for info in lib_chunk.download_track_info]
get_next_chunk = lib_chunk.HasField('continuation_token')
@utils.enforce_id_param
def download_song(self, song_id):
"""Download an uploaded or purchased song from your library.
Subscription tracks can't be downloaded with this method.
Returns a tuple ``(u'suggested_filename', 'audio_bytestring')``.
The filename
will be what the Music Manager would save the file as,
presented as a unicode string with the proper file extension.
You don't have to use it if you don't want.
:param song_id: a single uploaded or purchased song id.
To write the song to disk, use something like::
filename, audio = mm.download_song(an_id)
# if open() throws a UnicodeEncodeError, either use
# filename.encode('utf-8')
# or change your default encoding to something sane =)
with open(filename, 'wb') as f:
f.write(audio)
Unlike with :py:func:`Webclient.get_song_download_info
`,
there is no download limit when using this interface.
Also unlike the Webclient, downloading a track requires authentication.
Returning a url does not suffice, since retrieving a track without auth
will produce an http 500.
"""
url = self._make_call(musicmanager.GetDownloadLink,
song_id,
self.uploader_id)['url']
response = self._make_call(musicmanager.DownloadTrack, url)
cd_header = response.headers['content-disposition']
filename = unquote(cd_header.split("filename*=UTF-8''")[-1])
return (filename, response.content)
def get_quota(self):
"""Returns a tuple of (number of uploaded tracks, allowed number of uploaded tracks)."""
if self.uploader_id is None:
raise NotLoggedIn("Not authenticated as an upload device;"
" run Musicmanager.login(...perform_upload_auth=True...)"
" first.")
client_state = self._make_call(
musicmanager.GetClientState, self.uploader_id).clientstate_response
return (client_state.total_track_count, client_state.locker_track_limit)
@utils.accept_singleton(basestring)
@utils.empty_arg_shortcircuit(return_code='{}')
def upload(self, filepaths, enable_matching=False,
enable_transcoding=True, transcode_quality='320k'):
"""Uploads the given filepaths.
All non-mp3 files will be transcoded before being uploaded.
This is a limitation of Google's backend.
An available installation of ffmpeg or avconv is required in most cases:
see `the installation page
`__ for details.
Returns a 3-tuple ``(uploaded, matched, not_uploaded)`` of dictionaries, eg::
(
{'': ''}, # uploaded
{'': ''}, # matched
{'': ''} # not uploaded
)
:param filepaths: a list of filepaths, or a single filepath.
:param enable_matching: if ``True``, attempt to use `scan and match
`__
to avoid uploading every song.
This requires ffmpeg or avconv.
**WARNING**: currently, mismatched songs can *not* be fixed with the 'Fix Incorrect Match'
button nor :py:func:`report_incorrect_match
`.
They would have to be deleted and reuploaded with matching disabled
(or with the Music Manager).
Fixing matches from gmusicapi may be supported in a future release; see issue `#89
`__.
:param enable_transcoding:
if ``False``, non-MP3 files that aren't matched using `scan and match
`__
will not be uploaded.
:param transcode_quality: if int, pass to ffmpeg/avconv ``-q:a`` for libmp3lame
(`lower-better int,
`__).
If string, pass to ffmpeg/avconv ``-b:a`` (eg ``'128k'`` for an average bitrate of 128k).
The default is 320kbps cbr (the highest possible quality).
All Google-supported filetypes are supported; see `Google's documentation
`__.
If ``PERMANENT_ERROR`` is given as a not_uploaded reason, attempts to reupload will never
succeed. The file will need to be changed before the server will reconsider it; the easiest
way is to change metadata tags (it's not important that the tag be uploaded, just that the
contents of the file change somehow).
"""
if self.uploader_id is None or self.uploader_name is None:
raise NotLoggedIn("Not authenticated as an upload device;"
" run Api.login(...perform_upload_auth=True...)"
" first.")
# TODO there is way too much code in this function.
# To return.
uploaded = {}
matched = {}
not_uploaded = {}
# Gather local information on the files.
local_info = {} # {clientid: (path, Track)}
for path in filepaths:
try:
track = musicmanager.UploadMetadata.fill_track_info(path)
except BaseException as e:
self.logger.exception("problem gathering local info of '%r'", path)
user_err_msg = str(e)
if 'Non-ASCII strings must be converted to unicode' in str(e):
# This is a protobuf-specific error; they require either ascii or unicode.
# To keep behavior consistent, make no effort to guess - require users
# to decode first.
user_err_msg = ("nonascii bytestrings must be decoded to unicode"
" (error: '%s')" % user_err_msg)
not_uploaded[path] = user_err_msg
else:
local_info[track.client_id] = (path, track)
if not local_info:
return uploaded, matched, not_uploaded
# TODO allow metadata faking
# Upload metadata; the server tells us what to do next.
res = self._make_call(musicmanager.UploadMetadata,
[t for (path, t) in local_info.values()],
self.uploader_id)
# TODO checking for proper contents should be handled in verification
md_res = res.metadata_response
responses = [r for r in md_res.track_sample_response]
sample_requests = [req for req in md_res.signed_challenge_info]
# Send scan and match samples if requested.
for sample_request in sample_requests:
path, track = local_info[sample_request.challenge_info.client_track_id]
bogus_sample = None
if not enable_matching:
bogus_sample = b'' # just send empty bytes
try:
res = self._make_call(musicmanager.ProvideSample,
path, sample_request, track,
self.uploader_id, bogus_sample)
except (IOError, ValueError) as e:
self.logger.warning("couldn't create scan and match sample for '%r': %s",
path, str(e))
not_uploaded[path] = str(e)
else:
responses.extend(res.sample_response.track_sample_response)
# Read sample responses and prep upload requests.
to_upload = {} # {serverid: (path, Track, do_not_rematch?)}
for sample_res in responses:
path, track = local_info[sample_res.client_track_id]
if sample_res.response_code == upload_pb2.TrackSampleResponse.MATCHED:
self.logger.info("matched '%r' to sid %s", path, sample_res.server_track_id)
matched[path] = sample_res.server_track_id
if not enable_matching:
self.logger.error("'%r' was matched without matching enabled", path)
elif sample_res.response_code == upload_pb2.TrackSampleResponse.UPLOAD_REQUESTED:
to_upload[sample_res.server_track_id] = (path, track, False)
else:
# there was a problem
# report the symbolic name of the response code enum for debugging
enum_desc = upload_pb2._TRACKSAMPLERESPONSE.enum_types[0]
res_name = enum_desc.values_by_number[sample_res.response_code].name
err_msg = "TrackSampleResponse code %s: %s" % (sample_res.response_code, res_name)
if res_name == 'ALREADY_EXISTS':
# include the sid, too
# this shouldn't be relied on externally, but I use it in
# tests - being surrounded by parens is how it's matched
err_msg += "(%s)" % sample_res.server_track_id
self.logger.warning("upload of '%r' rejected: %s", path, err_msg)
not_uploaded[path] = err_msg
# Send upload requests.
if to_upload:
# TODO reordering requests could avoid wasting time waiting for reup sync
self._make_call(musicmanager.UpdateUploadState, 'start', self.uploader_id)
for server_id, (path, track, do_not_rematch) in to_upload.items():
# It can take a few tries to get an session.
should_retry = True
attempts = 0
while should_retry and attempts < 10:
session = self._make_call(musicmanager.GetUploadSession,
self.uploader_id, len(uploaded),
track, path, server_id, do_not_rematch)
attempts += 1
got_session, error_details = \
musicmanager.GetUploadSession.process_session(session)
if got_session:
self.logger.info("got an upload session for '%r'", path)
break
should_retry, reason, error_code = error_details
self.logger.debug("problem getting upload session: %s\ncode=%s retrying=%s",
reason, error_code, should_retry)
if error_code == 200 and do_not_rematch:
# reupload requests need to wait on a server sync
# 200 == already uploaded, so force a retry in this case
should_retry = True
time.sleep(6) # wait before retrying
else:
err_msg = "GetUploadSession error %s: %s" % (error_code, reason)
self.logger.warning("giving up on upload session for '%r': %s", path, err_msg)
not_uploaded[path] = err_msg
continue # to next upload
# got a session, do the upload
# this terribly inconsistent naming isn't my fault: Google--
session = session['sessionStatus']
external = session['externalFieldTransfers'][0]
session_url = external['putInfo']['url']
content_type = external.get('content_type', 'audio/mpeg')
if track.original_content_type != locker_pb2.Track.MP3:
if enable_transcoding:
try:
self.logger.info("transcoding '%r' to mp3", path)
contents = utils.transcode_to_mp3(path, quality=transcode_quality)
except (IOError, ValueError) as e:
self.logger.warning("error transcoding %r: %s", path, e)
not_uploaded[path] = "transcoding error: %s" % e
continue
else:
not_uploaded[path] = "transcoding disabled"
continue
else:
with open(path, 'rb') as f:
contents = f.read()
upload_response = self._make_call(musicmanager.UploadFile,
session_url, content_type, contents)
success = upload_response.get('sessionStatus', {}).get('state')
if success:
uploaded[path] = server_id
else:
# 404 == already uploaded? serverside check on clientid?
self.logger.debug("could not finalize upload of '%r'. response: %s",
path, upload_response)
not_uploaded[path] = 'could not finalize upload; details in log'
self._make_call(musicmanager.UpdateUploadState, 'stopped', self.uploader_id)
return uploaded, matched, not_uploaded
gmusicapi-12.1.1/gmusicapi/clients/shared.py 0000664 0001750 0001750 00000015557 13400371522 021327 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
from __future__ import print_function, division, absolute_import, unicode_literals
from builtins import * # noqa
from past.builtins import basestring
import logging
import os
from gmusicapi.utils import utils
from future.utils import with_metaclass
from oauth2client.client import OAuth2WebServerFlow
import oauth2client.file
import webbrowser
class _Base(with_metaclass(utils.DocstringInheritMeta, object)):
"""Factors out common client setup."""
_session_class = utils.NotImplementedField
num_clients = 0 # used to disambiguate loggers
def __init__(self, logger_basename, debug_logging, validate, verify_ssl):
"""
:param debug_logging: each Client has a ``logger`` member.
The logger is named ``gmusicapi.`` and
will propogate to the ``gmusicapi`` root logger.
If this param is ``True``, handlers will be configured to send
this client's debug log output to disk,
with warnings and above printed to stderr.
`Appdirs `__
``user_log_dir`` is used by default. Users can run::
from gmusicapi.utils import utils
print utils.log_filepath
to see the exact location on their system.
If ``False``, no handlers will be configured;
users must create their own handlers.
Completely ignoring logging is dangerous and not recommended.
The Google Music protocol can change at any time; if
something were to go wrong, the logs would be necessary for
recovery.
:param validate: if False, do not validate server responses against
known schemas. This helps to catch protocol changes, but requires
significant cpu work.
This arg is stored as ``self.validate`` and can be safely
modified at runtime.
:param verify_ssl: if False, exceptions will not be raised if there
are problems verifying SSL certificates.
Be wary of using this option; it's almost always better to
fix the machine's SSL configuration than to ignore errors.
"""
# this isn't correct if init is called more than once, so we log the
# client name below to avoid confusion for people reading logs
_Base.num_clients += 1
logger_name = "gmusicapi.%s%s" % (logger_basename,
_Base.num_clients)
self._cache = {}
self.logger = logging.getLogger(logger_name)
self.validate = validate
self._verify_ssl = verify_ssl
def setup_session(s):
s.verify = self._verify_ssl
self.session = self._session_class(rsession_setup=setup_session)
if debug_logging:
utils.configure_debug_log_handlers(self.logger)
self.logger.info("initialized")
self.logout()
def _make_call(self, protocol, *args, **kwargs):
"""Returns the response of a protocol.Call.
args/kwargs are passed to protocol.perform.
CallFailure may be raised."""
return protocol.perform(self.session, self.validate, *args, **kwargs)
def is_authenticated(self):
"""Returns ``True`` if the Api can make an authenticated request."""
return self.session.is_authenticated
def logout(self):
"""Forgets local authentication and cached properties in this Api instance.
Returns ``True`` on success."""
# note to clients: this will be called during __init__.
self.session.logout()
self._cache.clear() # Clear the instance of all cached properties.
self.logger.info("logged out")
return True
class _OAuthClient(_Base):
_path_sentinel = object()
# the default path for credential storage
OAUTH_FILEPATH = utils.NotImplementedField
@classmethod
def perform_oauth(cls, storage_filepath=_path_sentinel, open_browser=False):
"""Provides a series of prompts for a user to follow to authenticate.
Returns ``oauth2client.client.OAuth2Credentials`` when successful.
In most cases, this should only be run once per machine to store
credentials to disk, then never be needed again.
If the user refuses to give access,
``oauth2client.client.FlowExchangeError`` is raised.
:param storage_filepath: a filepath to write the credentials to,
or ``None``
to not write the credentials to disk (which is not recommended).
`Appdirs `__
``user_data_dir`` is used by default. Check the OAUTH_FILEPATH field
on this class to see the exact location that will be used.
:param open_browser: if True, attempt to open the auth url
in the system default web browser. The url will be printed
regardless of this param's setting.
This flow is intentionally very simple.
For complete control over the OAuth flow, pass an
``oauth2client.client.OAuth2Credentials``
to :func:`login` instead.
"""
if storage_filepath is cls._path_sentinel:
storage_filepath = cls.OAUTH_FILEPATH
flow = OAuth2WebServerFlow(**cls._session_class.oauth._asdict())
auth_uri = flow.step1_get_authorize_url()
print()
print("Visit the following url:\n %s" % auth_uri)
if open_browser:
print()
print('Opening your browser to it now...', end=' ')
webbrowser.open(auth_uri)
print('done.')
print("If you don't see your browser, you can just copy and paste the url.")
print()
code = input("Follow the prompts, then paste the auth code here and hit enter: ")
credentials = flow.step2_exchange(code)
if storage_filepath is not None:
if storage_filepath == cls.OAUTH_FILEPATH:
utils.make_sure_path_exists(os.path.dirname(cls.OAUTH_FILEPATH), 0o700)
storage = oauth2client.file.Storage(storage_filepath)
storage.put(credentials)
return credentials
def _oauth_login(self, oauth_credentials):
"""Return True on success."""
if isinstance(oauth_credentials, basestring):
oauth_file = oauth_credentials
if oauth_file == self.OAUTH_FILEPATH:
utils.make_sure_path_exists(os.path.dirname(self.OAUTH_FILEPATH), 0o700)
storage = oauth2client.file.Storage(oauth_file)
oauth_credentials = storage.get()
if oauth_credentials is None:
self.logger.warning("could not retrieve oauth credentials from '%r'", oauth_file)
return False
if not self.session.login(oauth_credentials):
self.logger.warning("failed to authenticate")
return False
self.logger.info("oauth successful")
return True
gmusicapi-12.1.1/gmusicapi/clients/webclient.py 0000664 0001750 0001750 00000040345 13374625453 022045 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
from __future__ import print_function, division, absolute_import, unicode_literals
from future.utils import PY3
from past.builtins import basestring
from builtins import * # noqa
import warnings
if PY3:
from urllib.parse import parse_qsl, urlparse
else:
from urlparse import parse_qsl, urlparse
import gmusicapi
from gmusicapi.clients.shared import _Base
from gmusicapi.exceptions import GmusicapiWarning
from gmusicapi.protocol import webclient
from gmusicapi.utils import utils
import gmusicapi.session
class Webclient(_Base):
"""Allows library management and streaming by posing as the
music.google.com webclient.
Uploading is not supported by this client (use the :class:`Musicmanager`
to upload).
Any methods in this class that are duplicated by
the :class:`Mobileclient` are deprecated, and will generate a
warning at runtime.
The following methods are *not* deprecated:
* :func:`get_shared_playlist_info`
* :func:`get_song_download_info`
* :func:`get_stream_urls`
* :func:`get_stream_audio`
* :func:`report_incorrect_match`
* :func:`upload_album_art`
"""
_session_class = gmusicapi.session.Webclient
def __init__(self, debug_logging=True, validate=True, verify_ssl=True):
warnings.warn(
"Webclient functionality is not tested nor well supported. "
"Use Mobileclient or Musicmanager if possible.",
GmusicapiWarning
)
super(Webclient, self).__init__(self.__class__.__name__,
debug_logging,
validate,
verify_ssl)
def login(self, email, password):
"""Authenticates the webclient.
Returns ``True`` on success, ``False`` on failure.
:param email: eg ``'test@gmail.com'`` or just ``'test'``.
:param password: the account's password.
This is not stored locally, and is sent securely over SSL.
App-specific passwords are not supported on the webclient.
Users who don't use two-factor auth will likely need to enable
`less secure login `__.
If this is needed, a warning will be logged during login (which will print to stderr
in the default logging configuration).
"""
if not self.session.login(email, password):
self.logger.info("failed to authenticate")
return False
self.logger.info("authenticated")
return True
def logout(self):
return super(Webclient, self).logout()
def get_shared_playlist_info(self, share_token):
"""
Returns a dictionary with four keys: author, description, num_tracks, and title.
:param share_token: from ``playlist['shareToken']``, or a playlist share
url (``https://play.google.com/music/playlist/``).
Note that tokens from urls will need to be url-decoded,
eg ``AM...%3D%3D`` becomes ``AM...==``.
"""
res = self._make_call(webclient.GetSharedPlaylist, '', share_token)
num_tracks = len(res[1][0])
md = res[1][1]
return {
u'author': md[8],
u'description': md[7],
u'num_tracks': num_tracks,
u'title': md[1],
}
@utils.enforce_id_param
def get_song_download_info(self, song_id):
"""Returns a tuple: ``('', )``.
:param song_id: a single song id.
``url`` will be ``None`` if the download limit is exceeded.
GM allows 2 downloads per song. The download count may not always be accurate,
and the 2 download limit seems to be loosely enforced.
This call alone does not count towards a download -
the count is incremented when ``url`` is retrieved.
"""
# TODO the protocol expects a list of songs - could extend with accept_singleton
info = self._make_call(webclient.GetDownloadInfo, [song_id])
url = info.get('url')
return (url, info["downloadCounts"][song_id])
@utils.enforce_id_param
def get_stream_urls(self, song_id):
"""Returns a list of urls that point to a streamable version of this song.
If you just need the audio and are ok with gmusicapi doing the download,
consider using :func:`get_stream_audio` instead.
This abstracts away the differences between different kinds of tracks:
* normal tracks return a single url
* All Access tracks return multiple urls, which must be combined
:param song_id: a single song id.
While acquiring the urls requires authentication, retreiving the
contents does not.
However, there are limitations on how the stream urls can be used:
* the urls expire after a minute
* only one IP can be streaming music at once.
Other attempts will get an http 403 with
``X-Rejected-Reason: ANOTHER_STREAM_BEING_PLAYED``.
*This is only intended for streaming*. The streamed audio does not contain metadata.
Use :func:`get_song_download_info` or :func:`Musicmanager.download_song
`
to download files with metadata.
"""
res = self._make_call(webclient.GetStreamUrl, song_id)
try:
return [res['url']]
except KeyError:
return res['urls']
@utils.enforce_id_param
def get_stream_audio(self, song_id, use_range_header=None):
"""Returns a bytestring containing mp3 audio for this song.
:param song_id: a single song id
:param use_range_header: in some cases, an HTTP range header can be
used to save some bandwidth.
However, there's no guarantee that the server will respect it,
meaning that the client may get back an unexpected response when
using it.
There are three possible values for this argument:
* None: (default) send header; fix response locally on problems
* True: send header; raise IOError on problems
* False: do not send header
"""
urls = self.get_stream_urls(song_id)
# TODO shouldn't session.send be used throughout?
if len(urls) == 1:
return self.session._rsession.get(urls[0]).content
# AA tracks are separated into multiple files.
# the url contains the range of each file to be used.
range_pairs = [[int(s) for s in val.split('-')]
for url in urls
for key, val in parse_qsl(urlparse(url)[4])
if key == 'range']
stream_pieces = bytearray()
prev_end = 0
headers = None
for url, (start, end) in zip(urls, range_pairs):
if use_range_header or use_range_header is None:
headers = {'Range': 'bytes=' + str(prev_end - start) + '-'}
audio = self.session._rsession.get(url, headers=headers).content
if end - prev_end != len(audio) - 1:
# content length is not in the right range
if use_range_header:
# the user didn't want automatic response fixup
raise IOError('use_range_header is True but the response'
' was not the correct content length.'
' This might be caused by a (poorly-written) http proxy.')
# trim to the proper range
audio = audio[prev_end - start:]
stream_pieces.extend(audio)
prev_end = end + 1
return bytes(stream_pieces)
@utils.accept_singleton(basestring)
@utils.enforce_ids_param
@utils.empty_arg_shortcircuit
def report_incorrect_match(self, song_ids):
"""Equivalent to the 'Fix Incorrect Match' button, this requests re-uploading of songs.
Returns the song_ids provided.
:param song_ids: a list of song ids to report, or a single song id.
Note that if you uploaded a song through gmusicapi, it won't be reuploaded
automatically - this currently only works for songs uploaded with the Music Manager.
See issue `#89 `__.
This should only be used on matched tracks (``song['type'] == 6``).
"""
self._make_call(webclient.ReportBadSongMatch, song_ids)
return song_ids
@utils.accept_singleton(basestring)
@utils.enforce_ids_param
@utils.empty_arg_shortcircuit
def upload_album_art(self, song_ids, image_filepath):
"""Uploads an image and sets it as the album art for songs.
Returns a url to the image on Google's servers.
:param song_ids: a list of song ids, or a single song id.
:param image_filepath: filepath of the art to use. jpg and png are known to work.
This function will *always* upload the provided image, even if it's already uploaded.
If the art is already uploaded and set for another song, copy over the
value of the ``'albumArtUrl'`` key using :func:`Mobileclient.change_song_metadata` instead.
"""
res = self._make_call(webclient.UploadImage, image_filepath)
url = res['imageUrl']
song_dicts = [dict((('id', id), ('albumArtUrl', url))) for id in song_ids]
self._make_call(webclient.ChangeSongMetadata, song_dicts)
return url
@utils.accept_singleton(dict)
@utils.empty_arg_shortcircuit
def change_song_metadata(self, songs):
"""Changes metadata of songs.
Returns a list of the song ids changed.
:param songs: a list of song dictionaries, each dictionary must contain valid song 'id'
The following fields are supported: title, album, albumArtist, artist
"""
self._make_call(webclient.ChangeSongMetadata, songs)
return list(song['id'] for song in songs)
# deprecated methods follow:
@utils.deprecated('prefer Mobileclient.create_playlist')
def create_playlist(self, name, description=None, public=False):
"""
Creates a playlist and returns its id.
:param name: the name of the playlist.
:param description: (optional) the description of the playlist.
:param public: if True and the user has All Access, create a shared playlist.
"""
res = self._make_call(webclient.CreatePlaylist, name, description, public)
return res[1][0]
@utils.deprecated('prefer Mobileclient.get_registered_devices')
def get_registered_devices(self):
"""
Returns a list of dictionaries representing devices associated with the account.
Performing the :class:`Musicmanager` OAuth flow will register a device
of type 1.
Installing the Google Music app on an android or ios device
and logging into it will register a device of type 2 or 3,
which is used for streaming with the :class:`Mobileclient`.
Here is an example response::
[
{
u'deviceType': 1, # laptop/desktop
u'id': u'00:11:22:33:AA:BB',
u'lastAccessedFormatted': u'May 24, 2015',
u'lastAccessedTimeMillis': 1432468588200, # utc-millisecond
u'lastEventTimeMillis': 1434211605335,
u'name': u'my computer'},
},
{
u'deviceType': 2, # android device
u'carrier': u'Google',
u'id': u'0x00112233aabbccdd', # remove 0x when streaming
u'lastAccessedFormatted': u'September 19, 2015',
u'lastAccessedTimeMillis': 1442706069906,
u'lastEventTimeMillis': 1435271137193,
u'manufacturer': u'Asus',
u'model': u'Nexus 7',
u'name': u'my nexus 7'
},
{
u'deviceType': 3, # ios device
u'id': u'ios:01234567-0123-0123-0123-0123456789AB',
u'lastAccessedFormatted': u'June 25, 2015',
u'lastAccessedTimeMillis': 1435271588780,
u'lastEventTimeMillis': 1435271442417,
u'name': u'my iphone'
}
]
"""
# TODO sessionid stuff
res = self._make_call(webclient.GetSettings, '')
return res['settings']['uploadDevice']
@utils.accept_singleton(basestring)
@utils.enforce_ids_param
@utils.empty_arg_shortcircuit
@utils.deprecated('prefer Mobileclient.delete_songs')
def delete_songs(self, song_ids):
"""**Deprecated**: prefer :func:`Mobileclient.delete_songs`.
Deletes songs from the entire library. Returns a list of deleted song ids.
:param song_ids: a list of song ids, or a single song id.
"""
res = self._make_call(webclient.DeleteSongs, song_ids)
return res['deleteIds']
@utils.accept_singleton(basestring, 2)
@utils.enforce_ids_param(2)
@utils.enforce_id_param
@utils.empty_arg_shortcircuit(position=2)
@utils.deprecated('prefer Mobileclient.add_songs_to_playlist')
def add_songs_to_playlist(self, playlist_id, song_ids):
"""**Deprecated**: prefer :func:`Mobileclient.add_songs_to_playlist`.
Appends songs to a playlist.
Returns a list of (song id, playlistEntryId) tuples that were added.
:param playlist_id: id of the playlist to add to.
:param song_ids: a list of song ids, or a single song id.
Playlists have a maximum size of 1000 songs.
"""
res = self._make_call(webclient.AddToPlaylist, playlist_id, song_ids)
new_entries = res['songIds']
return [(e['songId'], e['playlistEntryId']) for e in new_entries]
@utils.accept_singleton(basestring, 2)
@utils.enforce_ids_param(2)
@utils.enforce_id_param
@utils.empty_arg_shortcircuit(position=2)
@utils.deprecated('prefer Mobileclient.remove_entries_from_playlist')
def remove_songs_from_playlist(self, playlist_id, sids_to_match):
"""**Deprecated**: prefer :func:`Mobileclient.remove_entries_from_playlist`.
Removes all copies of the given song ids from a playlist.
Returns a list of removed (sid, eid) pairs.
:param playlist_id: id of the playlist to remove songs from.
:param sids_to_match: a list of song ids to match, or a single song id.
This does *not always* the inverse of a call to :func:`add_songs_to_playlist`,
since multiple copies of the same song are removed.
"""
playlist_tracks = self.get_playlist_songs(playlist_id)
sid_set = set(sids_to_match)
matching_eids = [t["playlistEntryId"]
for t in playlist_tracks
if t["id"] in sid_set]
if matching_eids:
# Call returns "sid_eid" strings.
sid_eids = self._remove_entries_from_playlist(playlist_id,
matching_eids)
return [s.split("_") for s in sid_eids]
else:
return []
@utils.accept_singleton(basestring, 2)
@utils.empty_arg_shortcircuit(position=2)
def _remove_entries_from_playlist(self, playlist_id, entry_ids_to_remove):
"""Removes entries from a playlist. Returns a list of removed "sid_eid" strings.
:param playlist_id: the playlist to be modified.
:param entry_ids: a list of entry ids, or a single entry id.
"""
# GM requires the song ids in the call as well; find them.
playlist_tracks = self.get_playlist_songs(playlist_id)
remove_eid_set = set(entry_ids_to_remove)
e_s_id_pairs = [(t["id"], t["playlistEntryId"])
for t in playlist_tracks
if t["playlistEntryId"] in remove_eid_set]
num_not_found = len(entry_ids_to_remove) - len(e_s_id_pairs)
if num_not_found > 0:
self.logger.warning("when removing, %d entry ids could not be found in playlist id %s",
num_not_found, playlist_id)
# Unzip the pairs.
sids, eids = list(zip(*e_s_id_pairs))
res = self._make_call(webclient.DeleteSongs, sids, playlist_id, eids)
return res['deleteIds']
gmusicapi-12.1.1/gmusicapi/exceptions.py 0000664 0001750 0001750 00000003247 13374625453 020611 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
"""Custom exceptions used across the project."""
from __future__ import print_function, division, absolute_import, unicode_literals
from future.utils import python_2_unicode_compatible
from builtins import * # noqa
@python_2_unicode_compatible
class CallFailure(Exception):
"""Exception raised when a Google Music server responds that a call failed.
Attributes:
callname -- name of the protocol.Call that failed
"""
def __init__(self, message, callname):
Exception.__init__(self, message)
self.callname = callname
def __str__(self):
return "%s: %s" % (self.callname, Exception.__str__(self))
class ParseException(Exception):
"""Thrown by Call.parse_response on errors."""
pass
class ValidationException(Exception):
"""Thrown by Transaction.verify_res_schema on errors."""
pass
class AlreadyLoggedIn(Exception):
pass
class NotLoggedIn(Exception):
pass
class NotSubscribed(Exception):
def __init__(self, *args):
if len(args) >= 1:
args = list(args)
args[0] += " (https://goo.gl/v1wVHT)"
args = tuple(args)
else:
args = ("Subscription required. (https://goo.gl/v1wVHT)",)
self.args = args
class GmusicapiWarning(UserWarning):
pass
class InvalidDeviceId(Exception):
def __init__(self, message, ids):
if ids:
message += 'Your valid device IDs are:\n* %s' % '\n* '.join(ids)
else:
message += 'It looks like your account does not have any '
'valid device IDs.'
super(InvalidDeviceId, self).__init__(message)
self.valid_device_ids = ids
gmusicapi-12.1.1/gmusicapi/gmtools/ 0000755 0001750 0001750 00000000000 13512455420 017517 5 ustar simon simon 0000000 0000000 gmusicapi-12.1.1/gmusicapi/gmtools/__init__.py 0000600 0001750 0001750 00000000030 12667715127 021626 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
gmusicapi-12.1.1/gmusicapi/gmtools/tools.py 0000664 0001750 0001750 00000034504 13374625453 021254 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
"""Tools for manipulating client-received Google Music data."""
from __future__ import print_function, division, absolute_import, unicode_literals
from builtins import * # noqa
import operator
import re
import collections
from collections import Counter
from functools import reduce
def get_id_pairs(track_list):
"""Create a list of (sid, eid) tuples from a list of tracks.
Tracks without an eid will have an eid of None."""
return [(t["id"], t.get("playlistEntryId")) for t in track_list]
def find_playlist_changes(orig_tracks, modified_tracks):
"""Finds the changes between two playlists.
Returns a tuple of (deletions, additions, staying).
Deletions and additions are both Counters of (sid, eid) tuples;
staying is a set of (sid, eid) tuples.
:param old: the original playlist.
:param modified: the modified playlist."""
s_pairs = get_id_pairs(orig_tracks)
# Three cases for desired pairs:
# 1: (sid, eid from this playlist): either no action or add
# (if someone adds a dupe from the same playlist)
# 2: (sid, eid not from this playlist): add
# 3: (sid, None): add
d_pairs = get_id_pairs(modified_tracks)
# Counters are multisets.
s_count = Counter(s_pairs)
d_count = Counter(d_pairs)
to_del = s_count - d_count
to_add = d_count - s_count
to_keep = set(s_count & d_count) # guaranteed to be counts of 1
return (to_del, to_add, to_keep)
def filter_song_md(song, md_list=['id'], no_singletons=True):
"""Returns a list of desired metadata from a song.
Does not modify the given song.
:param song: Dictionary representing a GM song.
:param md_list: (optional) the ordered list of metadata to select.
:param no_singletons: (optional) if md_list is of length 1, return the data,
not a singleton list.
"""
filtered = [song[md_type] for md_type in md_list]
if len(md_list) == 1 and no_singletons:
return filtered[0]
else:
return filtered
def build_song_rep(song, md_list=['title', 'artist', 'album'], divider=" - "):
"""Returns a string of the requested metadata types.
The order of md_list determines order in the string.
:param song: Dictionary representing a GM song.
:param md_list: (optional) list of valid GM metadata types.
:param divider: (optional) string to join the metadata.
"""
filtered = filter_song_md(song, md_list, no_singletons=False)
return divider.join(filtered)
def reorder_to(l, order):
"""Returns a list, reordered to a specific ordering.
:param l: the list to reorder. It is not modified.
:param order: a list containing the new ordering,
eg [2,1,0] to reverse a list of length 3
"""
# Zip on ordering, sort by it, then remove ordering.
return [el[1] for el in sorted(zip(order, l), key=lambda el: el[0])]
def build_queries_from(f, regex, cap_types, cap_pr, encoding='ascii'):
"""Returns a list of queries from the given file.
Queries have the form [(, ), ...]
:param f: opened file, ready to read.
:param regex: a compiled regex to capture query info from file lines.
:param cap_types: the GM metadata types of the regex captures.
:param cap_pr: the priority of the captures.
:param encoding: (optional) encoding of the file.
"""
queries = []
for line in f:
matches = regex.match(line)
if matches:
# Zip captures to their types and order by priority to build a query.
query = reorder_to(
list(zip(matches.groups(), cap_types)),
cap_pr)
queries.append(query)
return queries
def build_query_rep(query, divider=" - "):
"""Build a string representation of a query, without metadata types"""
return divider.join([el[0] for el in query])
# Not mine. From: http://en.wikipedia.org/wiki/Function_composition_(computer_science)
def compose(*funcs, **kfuncs):
"""Compose a group of functions (f(g(h(..)))) into (fogoh...)(...)"""
return reduce(lambda f, g: lambda *args, **kaargs: f(g(*args, **kaargs)), funcs)
class SongMatcher(object):
"""Matches GM songs to user-provided metadata."""
def __init__(self, songs, log_metadata=['title', 'artist', 'album']):
"""Prepares songs for matching and determines logging options.
:param songs: list of GM songs to match against.
:param log_metadata: list of valid GM metadata types to show in the log.
order given will be order outputted.
"""
# If match times are a problem, could
# read to an indexed format here.
self.library = songs
# Lines of a log of how matching went.
self.log_lines = []
self.log_metadata = log_metadata
def build_log(self):
"""Returns a string built from the current log lines."""
encoded_lines = [line.encode('utf-8') for line in self.log_lines]
return "\n".join(encoded_lines)
def build_song_for_log(self, song):
"""Returns a string built from a song using log options.
:param song:
"""
return build_song_rep(song, self.log_metadata)
class SearchModifier(object):
"""Controls how to query the library.
Implementations define a comparator, and 2 functions
(transformers) to modify the query and song data on the fly.
Sometimes it makes sense to chain implementations.
In this case, transformers are composed and the most
outward comparator is used.
"""
def __init__(self, q_t, s_t, comp):
# Comparator - defines how to compare query and song data.
# f(song data, query) -> truthy value
self.comp = comp
# Query and song transformers -
# manipulate query, song before comparison.
# f(unicode) -> unicode
self.q_t = q_t
self.s_t = s_t
# Some modifiers that are useful in my library:
# Ignore capitalization:
ignore_caps = SearchModifier(
# Change query and song to lowercase,
# before comparing with ==.
str.lower,
str.lower,
operator.eq
)
# Wildcard punctuation (also non ascii chars):
ignore_punc = SearchModifier(
# Replace query with a regex, where punc matches any (or no) characters.
lambda q: re.sub(r"[^a-zA-Z0-9\s]", ".*", q),
# Don't change the song.
lambda s: s,
# The comparator becomes regex matching.
lambda sd, q: re.search(q, sd)
)
implemented_modifiers = (ignore_caps, ignore_punc)
# The modifiers and order to be used in auto query mode.
auto_modifiers = implemented_modifiers
# Tiebreakers are used when there are multiple results from a query.
@staticmethod
def manual_tiebreak(query, results):
"""Prompts a user to choose a result from multiple.
For use with query_library as a tiebreaker.
Returns a singleton list or None.
:param query: the original query.
:param results: list of results.
"""
print()
print("Manual tiebreak for query:")
print(build_query_rep(query).encode('utf-8'))
print()
print("Enter the number next to your choice:")
print()
print("0: None of these.")
menu_lines = []
key = 1
for song in results:
menu_lines.append(
str(key) +
": " +
build_song_rep(song).encode('utf-8'))
key += 1
print("\n".join(menu_lines))
choice = -1
while not (0 <= choice <= len(results)):
try:
choice = int(input("Choice: "))
except ValueError:
pass
return None if choice == 0 else [results[choice - 1]]
# Tiebreaker which does nothing with results.
@staticmethod
def no_tiebreak(query, results):
return results
# Exception thrown when a tie is broken.
class TieBroken(Exception):
def __init__(self, results):
self.results = results
# A named tuple to hold the frozen args when querying recursively.
QueryState = collections.namedtuple('QueryState', 'orig t_breaker mods auto')
def query_library(self, query, tie_breaker=no_tiebreak, modifiers=None, auto=False):
"""Queries the library for songs.
returns a list of matches, or None.
"""
if not modifiers:
modifiers = []
try:
if not auto:
return self.query_library_rec(query, self.library,
self.QueryState(query, tie_breaker, modifiers, auto))
else:
# Auto mode attempts a search with the current modifiers.
# If we get 1 result, we return it.
# If we get no results, we add the next mod from auto_modifers and try again.
# If we get many results, we branch and try with another modifier.
# On no results, we tiebreak our old results.
# Otherwise, we return the branched results.
current_mods = modifiers[:]
# Be ready to use any mods from the auto list which we aren't using already.
future_mods = (m for m in self.auto_modifiers if m not in modifiers)
while True: # broken when future_mods runs out
# will not break ties in auto mode
results = self.query_library_rec(
query, self.library,
self.QueryState(query, tie_breaker, current_mods, auto))
if not results:
try:
current_mods.append(next(future_mods))
except StopIteration:
return results
elif len(results) == 1:
return results
else:
# Received many results from our current search.
# Branch; try more modifers to try and improve.
# If results, use them; otherwise tiebreak ours.
try:
current_mods.append(next(future_mods))
except StopIteration:
raise self.TieBroken(tie_breaker.__func__(query, results))
next_results = self.query_library(query, tie_breaker, current_mods, auto)
if not next_results:
raise self.TieBroken(tie_breaker.__func__(query, results))
else:
return next_results
except self.TieBroken as tie:
return tie.results
def query_library_rec(self, query, library, state):
"""Returns a list of matches, or None.
Recursive querying routine for query_library.
"""
if len(query) == 0:
return None
# Composing applies right to left; currently mods are left to right.
# Reverse then append the default modifier for proper compose order.
mods_to_apply = [sm for sm in reversed(state.mods)]
mods_to_apply.append(self.SearchModifier(
lambda q: q,
lambda sd: sd,
operator.eq))
# Create the transformers by composing all of them.
q_t = compose(*list(map((lambda sm: sm.q_t), mods_to_apply)))
s_t = compose(*list(map((lambda sm: sm.s_t), mods_to_apply)))
# Use the most outward comparator.
comp = mods_to_apply[0].comp
q, md_type = query[0]
# No need to repeatedly transform q.
q_transformed = q_t(q)
# GM limits libraries to 20k songs; this isn't a big performance hit.
results = [s for s in library if comp(s_t(s[md_type]), q_transformed)]
# Check for immediate return conditions.
if not results:
return None
if len(results) == 1:
return [results[0]]
# Try to refine results by querying them with the next metadata in the query.
next_query = query[1:]
next_results = self.query_library_rec(next_query, results, state)
if not next_results:
# Don't break ties in auto mode; it's handled a level up.
if not state.auto:
raise self.TieBroken(state.t_breaker(state.orig, results))
else:
return results
# Now we have multiple for both our query and the next.
# Always prefer the next query to ours.
return next_results
def match(self, queries, tie_breaker=manual_tiebreak, auto=True):
"""Runs queries against the library; returns a list of songs.
Match success is logged.
:param query: list of (query, metadata type) in order of precedence.
eg [('The Car Song', 'title'), ('The Cat Empire', 'artist')]
:param tie_breaker: (optional) tie breaker to use.
:param modifiers: (optional) An ordered collection of SearchModifers.
Applied during the query left to right.
:param auto: (optional) When True, automagically manage modifiers to find results.
"""
matches = []
self.log_lines.append("## Starting match of " + str(len(queries)) + " queries ##")
for query in queries:
res = self.query_library(query, tie_breaker, auto=auto)
if res:
matches += res
# Log the results.
# The alert precedes the information for a quick view of what happened.
alert = None
if res is None:
alert = "!!"
elif len(res) == 1:
alert = "=="
else:
alert = "??"
# Each query shows the alert and the query.
self.log_lines.append(alert + " " + build_query_rep(query))
# Displayed on the line below the alert (might be useful later).
extra_info = None
if res:
for song in res:
self.log_lines.append(
(extra_info if extra_info else (' ' * len(alert))) +
" " +
self.build_song_for_log(song))
elif extra_info:
self.log_lines.append(extra_info)
return matches
gmusicapi-12.1.1/gmusicapi/protocol/ 0000755 0001750 0001750 00000000000 13512455420 017674 5 ustar simon simon 0000000 0000000 gmusicapi-12.1.1/gmusicapi/protocol/__init__.py 0000600 0001750 0001750 00000000030 12667715127 022003 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
gmusicapi-12.1.1/gmusicapi/protocol/download_pb2.py 0000664 0001750 0001750 00000032057 13374625453 022644 0 ustar simon simon 0000000 0000000 # Generated by the protocol buffer compiler. DO NOT EDIT!
# source: download.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
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 import descriptor_pb2
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='download.proto',
package='wireless_android_skyjam',
syntax='proto2',
serialized_pb=_b('\n\x0e\x64ownload.proto\x12\x17wireless_android_skyjam\"\xf7\x01\n\x18GetTracksToExportRequest\x12\x11\n\tclient_id\x18\x02 \x02(\t\x12\x1a\n\x12\x63ontinuation_token\x18\x03 \x01(\t\x12Y\n\x0b\x65xport_type\x18\x04 \x01(\x0e\x32\x44.wireless_android_skyjam.GetTracksToExportRequest.TracksToExportType\x12\x13\n\x0bupdated_min\x18\x05 \x01(\x03\"<\n\x12TracksToExportType\x12\x07\n\x03\x41LL\x10\x01\x12\x1d\n\x19PURCHASED_AND_PROMOTIONAL\x10\x02\"\xbc\x01\n\x11\x44ownloadTrackInfo\x12\n\n\x02id\x18\x01 \x01(\t\x12\r\n\x05title\x18\x02 \x01(\t\x12\r\n\x05\x61lbum\x18\x03 \x01(\t\x12\x14\n\x0c\x61lbum_artist\x18\x04 \x01(\t\x12\x0e\n\x06\x61rtist\x18\x05 \x01(\t\x12\x14\n\x0ctrack_number\x18\x06 \x01(\x05\x12\x12\n\ntrack_size\x18\x07 \x01(\x03\x12\x13\n\x0b\x64isc_number\x18\x08 \x01(\x05\x12\x18\n\x10total_disc_count\x18\t \x01(\x05\"\x83\x03\n\x19GetTracksToExportResponse\x12W\n\x06status\x18\x01 \x02(\x0e\x32G.wireless_android_skyjam.GetTracksToExportResponse.TracksToExportStatus\x12G\n\x13\x64ownload_track_info\x18\x02 \x03(\x0b\x32*.wireless_android_skyjam.DownloadTrackInfo\x12\x1a\n\x12\x63ontinuation_token\x18\x03 \x01(\t\x12\x13\n\x0bupdated_min\x18\x04 \x01(\x03\"\x92\x01\n\x14TracksToExportStatus\x12\x06\n\x02OK\x10\x01\x12\x13\n\x0fTRANSIENT_ERROR\x10\x02\x12\x1b\n\x17MAX_NUM_CLIENTS_REACHED\x10\x03\x12!\n\x1dUNABLE_TO_AUTHENTICATE_CLIENT\x10\x04\x12\x1d\n\x19UNABLE_TO_REGISTER_CLIENT\x10\x05')
)
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
_GETTRACKSTOEXPORTREQUEST_TRACKSTOEXPORTTYPE = _descriptor.EnumDescriptor(
name='TracksToExportType',
full_name='wireless_android_skyjam.GetTracksToExportRequest.TracksToExportType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='ALL', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PURCHASED_AND_PROMOTIONAL', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=231,
serialized_end=291,
)
_sym_db.RegisterEnumDescriptor(_GETTRACKSTOEXPORTREQUEST_TRACKSTOEXPORTTYPE)
_GETTRACKSTOEXPORTRESPONSE_TRACKSTOEXPORTSTATUS = _descriptor.EnumDescriptor(
name='TracksToExportStatus',
full_name='wireless_android_skyjam.GetTracksToExportResponse.TracksToExportStatus',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='TRANSIENT_ERROR', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MAX_NUM_CLIENTS_REACHED', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UNABLE_TO_AUTHENTICATE_CLIENT', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UNABLE_TO_REGISTER_CLIENT', index=4, number=5,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=726,
serialized_end=872,
)
_sym_db.RegisterEnumDescriptor(_GETTRACKSTOEXPORTRESPONSE_TRACKSTOEXPORTSTATUS)
_GETTRACKSTOEXPORTREQUEST = _descriptor.Descriptor(
name='GetTracksToExportRequest',
full_name='wireless_android_skyjam.GetTracksToExportRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.GetTracksToExportRequest.client_id', index=0,
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,
options=None),
_descriptor.FieldDescriptor(
name='continuation_token', full_name='wireless_android_skyjam.GetTracksToExportRequest.continuation_token', index=1,
number=3, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='export_type', full_name='wireless_android_skyjam.GetTracksToExportRequest.export_type', index=2,
number=4, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='updated_min', full_name='wireless_android_skyjam.GetTracksToExportRequest.updated_min', index=3,
number=5, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_GETTRACKSTOEXPORTREQUEST_TRACKSTOEXPORTTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=44,
serialized_end=291,
)
_DOWNLOADTRACKINFO = _descriptor.Descriptor(
name='DownloadTrackInfo',
full_name='wireless_android_skyjam.DownloadTrackInfo',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.DownloadTrackInfo.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,
options=None),
_descriptor.FieldDescriptor(
name='title', full_name='wireless_android_skyjam.DownloadTrackInfo.title', 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,
options=None),
_descriptor.FieldDescriptor(
name='album', full_name='wireless_android_skyjam.DownloadTrackInfo.album', 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,
options=None),
_descriptor.FieldDescriptor(
name='album_artist', full_name='wireless_android_skyjam.DownloadTrackInfo.album_artist', 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,
options=None),
_descriptor.FieldDescriptor(
name='artist', full_name='wireless_android_skyjam.DownloadTrackInfo.artist', 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,
options=None),
_descriptor.FieldDescriptor(
name='track_number', full_name='wireless_android_skyjam.DownloadTrackInfo.track_number', index=5,
number=6, 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,
options=None),
_descriptor.FieldDescriptor(
name='track_size', full_name='wireless_android_skyjam.DownloadTrackInfo.track_size', index=6,
number=7, 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,
options=None),
_descriptor.FieldDescriptor(
name='disc_number', full_name='wireless_android_skyjam.DownloadTrackInfo.disc_number', index=7,
number=8, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='total_disc_count', full_name='wireless_android_skyjam.DownloadTrackInfo.total_disc_count', index=8,
number=9, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=294,
serialized_end=482,
)
_GETTRACKSTOEXPORTRESPONSE = _descriptor.Descriptor(
name='GetTracksToExportResponse',
full_name='wireless_android_skyjam.GetTracksToExportResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='status', full_name='wireless_android_skyjam.GetTracksToExportResponse.status', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='download_track_info', full_name='wireless_android_skyjam.GetTracksToExportResponse.download_track_info', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='continuation_token', full_name='wireless_android_skyjam.GetTracksToExportResponse.continuation_token', 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,
options=None),
_descriptor.FieldDescriptor(
name='updated_min', full_name='wireless_android_skyjam.GetTracksToExportResponse.updated_min', index=3,
number=4, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_GETTRACKSTOEXPORTRESPONSE_TRACKSTOEXPORTSTATUS,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=485,
serialized_end=872,
)
_GETTRACKSTOEXPORTREQUEST.fields_by_name['export_type'].enum_type = _GETTRACKSTOEXPORTREQUEST_TRACKSTOEXPORTTYPE
_GETTRACKSTOEXPORTREQUEST_TRACKSTOEXPORTTYPE.containing_type = _GETTRACKSTOEXPORTREQUEST
_GETTRACKSTOEXPORTRESPONSE.fields_by_name['status'].enum_type = _GETTRACKSTOEXPORTRESPONSE_TRACKSTOEXPORTSTATUS
_GETTRACKSTOEXPORTRESPONSE.fields_by_name['download_track_info'].message_type = _DOWNLOADTRACKINFO
_GETTRACKSTOEXPORTRESPONSE_TRACKSTOEXPORTSTATUS.containing_type = _GETTRACKSTOEXPORTRESPONSE
DESCRIPTOR.message_types_by_name['GetTracksToExportRequest'] = _GETTRACKSTOEXPORTREQUEST
DESCRIPTOR.message_types_by_name['DownloadTrackInfo'] = _DOWNLOADTRACKINFO
DESCRIPTOR.message_types_by_name['GetTracksToExportResponse'] = _GETTRACKSTOEXPORTRESPONSE
GetTracksToExportRequest = _reflection.GeneratedProtocolMessageType('GetTracksToExportRequest', (_message.Message,), dict(
DESCRIPTOR = _GETTRACKSTOEXPORTREQUEST,
__module__ = 'download_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetTracksToExportRequest)
))
_sym_db.RegisterMessage(GetTracksToExportRequest)
DownloadTrackInfo = _reflection.GeneratedProtocolMessageType('DownloadTrackInfo', (_message.Message,), dict(
DESCRIPTOR = _DOWNLOADTRACKINFO,
__module__ = 'download_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.DownloadTrackInfo)
))
_sym_db.RegisterMessage(DownloadTrackInfo)
GetTracksToExportResponse = _reflection.GeneratedProtocolMessageType('GetTracksToExportResponse', (_message.Message,), dict(
DESCRIPTOR = _GETTRACKSTOEXPORTRESPONSE,
__module__ = 'download_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetTracksToExportResponse)
))
_sym_db.RegisterMessage(GetTracksToExportResponse)
# @@protoc_insertion_point(module_scope)
gmusicapi-12.1.1/gmusicapi/protocol/locker_pb2.py 0000664 0001750 0001750 00000707113 13374625453 022316 0 ustar simon simon 0000000 0000000 # Generated by the protocol buffer compiler. DO NOT EDIT!
# source: locker.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
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 import descriptor_pb2
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
from . import uits_pb2 as uits__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
name='locker.proto',
package='wireless_android_skyjam',
syntax='proto2',
serialized_pb=_b('\n\x0clocker.proto\x12\x17wireless_android_skyjam\x1a\nuits.proto\"\x90\x02\n\x08\x41udioRef\x12\x36\n\x05store\x18\x01 \x02(\x0e\x32\'.wireless_android_skyjam.AudioRef.Store\x12\x0b\n\x03ref\x18\x02 \x02(\x0c\x12\x0b\n\x03url\x18\x04 \x01(\t\x12\x10\n\x08\x62it_rate\x18\x05 \x01(\x05\x12\x13\n\x0bsample_rate\x18\x06 \x01(\x05\x12\x14\n\x0c\x64ownloadable\x18\x07 \x01(\x08\x12\x17\n\x0f\x64uration_millis\x18\x08 \x01(\x03\x12\x19\n\x11rematch_timestamp\x18\t \x01(\x03\x12\x1e\n\x16invalid_due_to_wipeout\x18\n \x01(\x08\"!\n\x05Store\x12\r\n\tBLOBSTORE\x10\x01\x12\t\n\x05SM_V2\x10\x02\"\x81\x02\n\x08ImageRef\x12\x36\n\x05store\x18\x01 \x01(\x0e\x32\'.wireless_android_skyjam.ImageRef.Store\x12\r\n\x05width\x18\x02 \x01(\r\x12\x0e\n\x06height\x18\x03 \x01(\r\x12\x0b\n\x03url\x18\x06 \x01(\t\x12\x1e\n\x16invalid_due_to_wipeout\x18\x07 \x01(\x08\x12\x38\n\x06origin\x18\x08 \x01(\x0e\x32(.wireless_android_skyjam.ImageRef.Origin\"\x14\n\x05Store\x12\x0b\n\x07SHOEBOX\x10\x03\"!\n\x06Origin\x12\x0c\n\x08PERSONAL\x10\x01\x12\t\n\x05STORE\x10\x02\"1\n\x12UploadedUitsId3Tag\x12\r\n\x05owner\x18\x01 \x01(\t\x12\x0c\n\x04\x64\x61ta\x18\x02 \x01(\x0c\"5\n\x12\x41\x64\x64itionalMetadata\x12\x10\n\x08tag_name\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\x0c\"\x89\x01\n\x0bTrackExtras\x12H\n\x13\x61\x64\x64itional_metadata\x18\x02 \x03(\x0b\x32+.wireless_android_skyjam.AdditionalMetadata\x12\x12\n\ncandidates\x18\x03 \x03(\t\x12\x1c\n\x14original_sample_rate\x18\x04 \x01(\x05\"\xb1\x13\n\x05Track\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tclient_id\x18\x02 \x01(\t\x12\x1a\n\x12\x63reation_timestamp\x18\x03 \x01(\x03\x12\x1f\n\x17last_modified_timestamp\x18\x04 \x01(\x03\x12\x16\n\x07\x64\x65leted\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\r\n\x05title\x18\x06 \x01(\t\x12\x0e\n\x06\x61rtist\x18\x07 \x01(\t\x12\x13\n\x0b\x61rtist_hash\x18. \x01(\x03\x12\x10\n\x08\x63omposer\x18\x08 \x01(\t\x12\r\n\x05\x61lbum\x18\t \x01(\t\x12\x14\n\x0c\x61lbum_artist\x18\n \x01(\t\x12\x17\n\x0f\x63\x61nonical_album\x18\x38 \x01(\t\x12\x18\n\x10\x63\x61nonical_artist\x18\x39 \x01(\t\x12\x1d\n\x15\x63\x61nonical_genre_album\x18: \x01(\t\x12\x0c\n\x04year\x18\x0b \x01(\x05\x12\x0f\n\x07\x63omment\x18\x0c \x01(\t\x12\x14\n\x0ctrack_number\x18\r \x01(\x05\x12\r\n\x05genre\x18\x0e \x01(\t\x12\x17\n\x0f\x64uration_millis\x18\x0f \x01(\x03\x12\x18\n\x10\x62\x65\x61ts_per_minute\x18\x10 \x01(\x05\x12\x19\n\x11original_bit_rate\x18, \x01(\x05\x12\x34\n\taudio_ref\x18\x11 \x03(\x0b\x32!.wireless_android_skyjam.AudioRef\x12\x38\n\ralbum_art_ref\x18\x12 \x03(\x0b\x32!.wireless_android_skyjam.ImageRef\x12N\n\x13\x61vailability_status\x18\x13 \x01(\x0e\x32\x31.wireless_android_skyjam.Track.AvailabilityStatus\x12\x12\n\nplay_count\x18\x14 \x01(\x05\x12@\n\x0c\x63ontent_type\x18\x19 \x01(\x0e\x32*.wireless_android_skyjam.Track.ContentType\x12\x19\n\x11total_track_count\x18\x1a \x01(\x05\x12\x13\n\x0b\x64isc_number\x18\x1b \x01(\x05\x12\x18\n\x10total_disc_count\x18\x1c \x01(\x05\x12\x39\n\x08\x63hannels\x18\x1d \x01(\x0e\x32\'.wireless_android_skyjam.Track.Channels\x12<\n\ntrack_type\x18\x1e \x01(\x0e\x32(.wireless_android_skyjam.Track.TrackType\x12\x1e\n\x16use_single_server_copy\x18; \x01(\x08\x12\x35\n\x06rating\x18\x1f \x01(\x0e\x32%.wireless_android_skyjam.Track.Rating\x12\x16\n\x0e\x65stimated_size\x18 \x01(\x03\x12\x10\n\x08store_id\x18! \x01(\t\x12\x12\n\nmetajam_id\x18\" \x01(\t\x12 \n\x15metajam_id_confidence\x18+ \x01(\x01:\x01\x30\x12\x0c\n\x04uits\x18# \x01(\t\x12<\n\ruits_metadata\x18( \x01(\x0b\x32%.wireless_android_skyjam.UitsMetadata\x12\x13\n\x0b\x63ompilation\x18$ \x01(\x08\x12\x19\n\x11\x63lient_date_added\x18% \x01(\x03\x12\x18\n\x10recent_timestamp\x18& \x01(\x03\x12\x1d\n\x0e\x64o_not_rematch\x18\' \x01(\x08:\x05\x66\x61lse\x12\x1b\n\x13\x66rom_album_purchase\x18) \x01(\x08\x12\x18\n\x10\x61lbum_metajam_id\x18* \x01(\t\x12\x16\n\x0etransaction_id\x18- \x01(\t\x12\x13\n\x0b\x64\x65\x62ug_track\x18/ \x01(\x08\x12\x18\n\x10normalized_title\x18\x30 \x01(\t\x12\x19\n\x11normalized_artist\x18\x31 \x01(\t\x12\x18\n\x10normalized_album\x18\x32 \x01(\t\x12\x1f\n\x17normalized_album_artist\x18\x33 \x01(\t\x12\"\n\x1anormalized_canonical_album\x18\x36 \x01(\t\x12#\n\x1bnormalized_canonical_artist\x18\x37 \x01(\t\x12\x13\n\x0buploader_id\x18\x34 \x01(\t\x12\x17\n\x0f\x63lient_album_id\x18\x35 \x01(\t\x12\x18\n\x10label_owner_code\x18< \x01(\t\x12I\n\x15original_content_type\x18= \x01(\x0e\x32*.wireless_android_skyjam.Track.ContentType\x12\x42\n\ruploaded_uits\x18G \x03(\x0b\x32+.wireless_android_skyjam.UploadedUitsId3Tag\x12\x42\n\rexplicit_type\x18J \x01(\x0e\x32+.wireless_android_skyjam.Track.ExplicitType\x12:\n\x0ctrack_extras\x18K \x01(\x0b\x32$.wireless_android_skyjam.TrackExtras\"\x86\x01\n\x12\x41vailabilityStatus\x12\x0b\n\x07PENDING\x10\x01\x12\x0b\n\x07MATCHED\x10\x02\x12\x14\n\x10UPLOAD_REQUESTED\x10\x03\x12\r\n\tAVAILABLE\x10\x04\x12\x12\n\x0e\x46ORCE_REUPLOAD\x10\x05\x12\x1d\n\x19UPLOAD_PERMANENTLY_FAILED\x10\x06\"W\n\x0b\x43ontentType\x12\x07\n\x03MP3\x10\x01\x12\x07\n\x03M4A\x10\x02\x12\x07\n\x03\x41\x41\x43\x10\x03\x12\x08\n\x04\x46LAC\x10\x04\x12\x07\n\x03OGG\x10\x05\x12\x07\n\x03WMA\x10\x06\x12\x07\n\x03M4P\x10\x07\x12\x08\n\x04\x41LAC\x10\x08\" \n\x08\x43hannels\x12\x08\n\x04MONO\x10\x01\x12\n\n\x06STEREO\x10\x02\"\x8b\x01\n\tTrackType\x12\x11\n\rMATCHED_TRACK\x10\x01\x12\x13\n\x0fUNMATCHED_TRACK\x10\x02\x12\x0f\n\x0bLOCAL_TRACK\x10\x03\x12\x13\n\x0fPURCHASED_TRACK\x10\x04\x12\x1f\n\x1bMETADATA_ONLY_MATCHED_TRACK\x10\x05\x12\x0f\n\x0bPROMO_TRACK\x10\x06\"e\n\x06Rating\x12\r\n\tNOT_RATED\x10\x01\x12\x0c\n\x08ONE_STAR\x10\x02\x12\r\n\tTWO_STARS\x10\x03\x12\x0f\n\x0bTHREE_STARS\x10\x04\x12\x0e\n\nFOUR_STARS\x10\x05\x12\x0e\n\nFIVE_STARS\x10\x06\"3\n\x0c\x45xplicitType\x12\x0c\n\x08\x45XPLICIT\x10\x01\x12\t\n\x05\x43LEAN\x10\x02\x12\n\n\x06\x45\x44ITED\x10\x03\"7\n\x06Tracks\x12-\n\x05track\x18\x01 \x03(\x0b\x32\x1e.wireless_android_skyjam.Track\"\xe4\x02\n\x08Playlist\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tclient_id\x18\x02 \x01(\t\x12\x1a\n\x12\x63reation_timestamp\x18\x03 \x01(\x03\x12\x1f\n\x17last_modified_timestamp\x18\x04 \x01(\x03\x12\x16\n\x07\x64\x65leted\x18\x05 \x01(\x08:\x05\x66\x61lse\x12\x0c\n\x04name\x18\x06 \x01(\t\x12\x45\n\rplaylist_type\x18\x07 \x01(\x0e\x32..wireless_android_skyjam.Playlist.PlaylistType\x12;\n\x10playlist_art_ref\x18\x08 \x01(\x0b\x32!.wireless_android_skyjam.ImageRef\x12\x18\n\x10recent_timestamp\x18\t \x01(\x03\"8\n\x0cPlaylistType\x12\x12\n\x0eUSER_GENERATED\x10\x01\x12\t\n\x05MAGIC\x10\x02\x12\t\n\x05PROMO\x10\x03\"\xde\x03\n\rPlaylistEntry\x12\x13\n\x0bplaylist_id\x18\x01 \x01(\t\x12\x19\n\x11\x61\x62solute_position\x18\x02 \x01(\x03\x12\x1c\n\x14place_after_entry_id\x18\x03 \x01(\t\x12\x10\n\x08track_id\x18\x04 \x01(\t\x12\n\n\x02id\x18\x05 \x01(\t\x12\x11\n\tclient_id\x18\x06 \x01(\t\x12\x1a\n\x12\x63reation_timestamp\x18\x07 \x01(\x03\x12\x1f\n\x17last_modified_timestamp\x18\x08 \x01(\x03\x12\x16\n\x07\x64\x65leted\x18\t \x01(\x08:\x05\x66\x61lse\x12`\n\x19relative_position_id_type\x18\n \x01(\x0e\x32=.wireless_android_skyjam.PlaylistEntry.RelativePositionIdType\x12-\n\x05track\x18\x0f \x01(\x0b\x32\x1e.wireless_android_skyjam.Track\x12\x1d\n\x15place_before_entry_id\x18\x10 \x01(\t\x12\x17\n\x0fstring_position\x18\x11 \x01(\t\"0\n\x16RelativePositionIdType\x12\n\n\x06SERVER\x10\x01\x12\n\n\x06\x43LIENT\x10\x02\"\x80\x04\n\x16TrackSearchRestriction\x12Q\n\tattribute\x18\x01 \x02(\x0e\x32>.wireless_android_skyjam.TrackSearchRestriction.TrackAttribute\x12\r\n\x05value\x18\x02 \x02(\t\x12W\n\x0f\x63omparison_type\x18\x03 \x02(\x0e\x32>.wireless_android_skyjam.TrackSearchRestriction.ComparisonType\"\xa6\x01\n\x0eTrackAttribute\x12\t\n\x05TITLE\x10\x01\x12\n\n\x06\x41RTIST\x10\x02\x12\t\n\x05\x41LBUM\x10\x03\x12\x10\n\x0c\x41LBUM_ARTIST\x10\x04\x12\t\n\x05GENRE\x10\x05\x12\x17\n\x13\x41VAILABILITY_STATUS\x10\x06\x12\x0e\n\nTRACK_TYPE\x10\x07\x12\x08\n\x04YEAR\x10\x08\x12\x0c\n\x08STORE_ID\x10\t\x12\x14\n\x10\x41LBUM_METAJAM_ID\x10\n\"\x81\x01\n\x0e\x43omparisonType\x12\t\n\x05\x45QUAL\x10\x00\x12\r\n\tNOT_EQUAL\x10\x01\x12\x10\n\x0cGREATER_THAN\x10\x02\x12\x11\n\rGREATER_EQUAL\x10\x03\x12\r\n\tLESS_THAN\x10\x04\x12\x0e\n\nLESS_EQUAL\x10\x05\x12\x11\n\rPARTIAL_MATCH\x10\x06\"\xa2\x02\n\x19TrackSearchRestrictionSet\x12S\n\x04type\x18\x01 \x01(\x0e\x32\x45.wireless_android_skyjam.TrackSearchRestrictionSet.RestrictionSetType\x12\x44\n\x0brestriction\x18\x02 \x03(\x0b\x32/.wireless_android_skyjam.TrackSearchRestriction\x12\x43\n\x07sub_set\x18\x03 \x03(\x0b\x32\x32.wireless_android_skyjam.TrackSearchRestrictionSet\"%\n\x12RestrictionSetType\x12\x07\n\x03\x41ND\x10\x00\x12\x06\n\x02OR\x10\x01\"\x98\x02\n\x0eTrackSortOrder\x12I\n\tattribute\x18\x01 \x01(\x0e\x32\x36.wireless_android_skyjam.TrackSortOrder.TrackAttribute\x12\x18\n\ndescending\x18\x02 \x01(\x08:\x04true\"\xa0\x01\n\x0eTrackAttribute\x12\x16\n\x12LAST_MODIFIED_TIME\x10\x01\x12\n\n\x06\x41RTIST\x10\x02\x12\t\n\x05\x41LBUM\x10\x03\x12\t\n\x05TITLE\x10\x04\x12\x10\n\x0cTRACK_NUMBER\x10\x06\x12\x0e\n\nPLAY_COUNT\x10\t\x12\x13\n\x0f\x44URATION_MILLIS\x10\n\x12\n\n\x06RATING\x10\x0b\x12\x11\n\rCREATION_TIME\x10\x0c\"\xde\x03\n\x10GetTracksRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12\x13\n\x0bupdated_min\x18\x02 \x01(\x03\x12\x17\n\x0finclude_deleted\x18\x03 \x01(\x08\x12\x13\n\x0bmax_results\x18\x04 \x01(\x05\x12\x1a\n\x12\x63ontinuation_token\x18\x05 \x01(\t\x12K\n\x12search_restriction\x18\x06 \x03(\x0b\x32/.wireless_android_skyjam.TrackSearchRestriction\x12;\n\nsort_order\x18\x07 \x03(\x0b\x32\'.wireless_android_skyjam.TrackSortOrder\x12K\n\x0frestriction_set\x18\x08 \x01(\x0b\x32\x32.wireless_android_skyjam.TrackSearchRestrictionSet\x12S\n\x10track_projection\x18\t \x01(\x0e\x32\x39.wireless_android_skyjam.GetTracksRequest.TrackProjection\".\n\x0fTrackProjection\x12\x08\n\x04\x46ULL\x10\x01\x12\x11\n\rFRONTEND_VIEW\x10\x02\"\x83\x02\n\x11GetTracksResponse\x12N\n\rresponse_code\x18\x01 \x02(\x0e\x32\x37.wireless_android_skyjam.GetTracksResponse.ResponseCode\x12-\n\x05track\x18\x02 \x03(\x0b\x32\x1e.wireless_android_skyjam.Track\x12\x1f\n\x17\x65stimated_total_results\x18\x03 \x01(\x03\x12\x1a\n\x12\x63ontinuation_token\x18\x04 \x01(\t\"2\n\x0cResponseCode\x12\x06\n\x02OK\x10\x01\x12\x10\n\x0cNOT_MODIFIED\x10\x02\x12\x08\n\x04GONE\x10\x03\"\xfc\x01\n\x19GetPlaylistEntriesRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12\x13\n\x0bupdated_min\x18\x02 \x01(\x03\x12\x17\n\x0finclude_deleted\x18\x03 \x01(\x08\x12\x13\n\x0bmax_results\x18\x04 \x01(\x05\x12\x1a\n\x12\x63ontinuation_token\x18\x05 \x01(\t\x12\x1a\n\x12playlist_id_filter\x18\x06 \x01(\t\x12)\n\x1ainclude_all_track_metadata\x18\x07 \x01(\x08:\x05\x66\x61lse\x12(\n\x1aonly_show_available_tracks\x18\x08 \x01(\x08:\x04true\"\xa6\x02\n\x1aGetPlaylistEntriesResponse\x12W\n\rresponse_code\x18\x01 \x02(\x0e\x32@.wireless_android_skyjam.GetPlaylistEntriesResponse.ResponseCode\x12>\n\x0eplaylist_entry\x18\x02 \x03(\x0b\x32&.wireless_android_skyjam.PlaylistEntry\x12\x1f\n\x17\x65stimated_total_results\x18\x03 \x01(\x03\x12\x1a\n\x12\x63ontinuation_token\x18\x04 \x01(\t\"2\n\x0cResponseCode\x12\x06\n\x02OK\x10\x01\x12\x10\n\x0cNOT_MODIFIED\x10\x02\x12\x08\n\x04GONE\x10\x03\"\xe0\x01\n\x11PlaylistSortOrder\x12O\n\tattribute\x18\x01 \x01(\x0e\x32<.wireless_android_skyjam.PlaylistSortOrder.PlaylistAttribute\x12\x19\n\ndescending\x18\x02 \x01(\x08:\x05\x66\x61lse\"_\n\x11PlaylistAttribute\x12\x16\n\x12LAST_MODIFIED_TIME\x10\x01\x12\t\n\x05TITLE\x10\x02\x12\x11\n\rCREATION_TIME\x10\x03\x12\x14\n\x10RECENT_TIMESTAMP\x10\x04\"\xc5\x01\n\x13GetPlaylistsRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12\x13\n\x0bupdated_min\x18\x02 \x01(\x03\x12\x17\n\x0finclude_deleted\x18\x03 \x01(\x08\x12\x13\n\x0bmax_results\x18\x04 \x01(\x05\x12\x1a\n\x12\x63ontinuation_token\x18\x05 \x01(\t\x12>\n\nsort_order\x18\x06 \x01(\x0b\x32*.wireless_android_skyjam.PlaylistSortOrder\"\x8f\x02\n\x14GetPlaylistsResponse\x12Q\n\rresponse_code\x18\x01 \x02(\x0e\x32:.wireless_android_skyjam.GetPlaylistsResponse.ResponseCode\x12\x33\n\x08playlist\x18\x02 \x03(\x0b\x32!.wireless_android_skyjam.Playlist\x12\x1f\n\x17\x65stimated_total_results\x18\x03 \x01(\x03\x12\x1a\n\x12\x63ontinuation_token\x18\x04 \x01(\t\"2\n\x0cResponseCode\x12\x06\n\x02OK\x10\x01\x12\x10\n\x0cNOT_MODIFIED\x10\x02\x12\x08\n\x04GONE\x10\x03\"3\n\x12LookupTrackRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tclient_id\x18\x02 \x01(\t\";\n\x1aLookupPlaylistEntryRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tclient_id\x18\x02 \x01(\t\"6\n\x15LookupPlaylistRequest\x12\n\n\x02id\x18\x01 \x01(\t\x12\x11\n\tclient_id\x18\x02 \x01(\t\"\x9e\x03\n\x12\x42\x61tchLookupRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12:\n\x05track\x18\x02 \x03(\x0b\x32+.wireless_android_skyjam.LookupTrackRequest\x12@\n\x08playlist\x18\x03 \x03(\x0b\x32..wireless_android_skyjam.LookupPlaylistRequest\x12O\n\rmetadata_type\x18\x04 \x01(\x0e\x32\x38.wireless_android_skyjam.BatchLookupRequest.MetadataType\x12K\n\x0eplaylist_entry\x18\x05 \x03(\x0b\x32\x33.wireless_android_skyjam.LookupPlaylistEntryRequest\x12\x1e\n\x0finclude_deleted\x18\x06 \x01(\x08:\x05\x66\x61lse\";\n\x0cMetadataType\x12\t\n\x05TRACK\x10\x01\x12\x0c\n\x08PLAYLIST\x10\x02\x12\x12\n\x0ePLAYLIST_ENTRY\x10\x03\"\xb9\x01\n\x13\x42\x61tchLookupResponse\x12-\n\x05track\x18\x01 \x03(\x0b\x32\x1e.wireless_android_skyjam.Track\x12\x33\n\x08playlist\x18\x02 \x03(\x0b\x32!.wireless_android_skyjam.Playlist\x12>\n\x0eplaylist_entry\x18\x03 \x03(\x0b\x32&.wireless_android_skyjam.PlaylistEntry\"\xea\x01\n\x12MutateTrackRequest\x12\x34\n\x0c\x63reate_track\x18\x01 \x01(\x0b\x32\x1e.wireless_android_skyjam.Track\x12\x34\n\x0cupdate_track\x18\x02 \x01(\x0b\x32\x1e.wireless_android_skyjam.Track\x12\x14\n\x0c\x64\x65lete_track\x18\x03 \x01(\t\x12\x16\n\x0epartial_update\x18\x04 \x01(\x08\x12\"\n\x14update_last_modified\x18\x05 \x01(\x08:\x04true\x12\x16\n\x0eundelete_track\x18\x06 \x01(\t\"\xe6\x03\n\x0eMutateResponse\x12Q\n\rresponse_code\x18\x01 \x01(\x0e\x32:.wireless_android_skyjam.MutateResponse.MutateResponseCode\x12\n\n\x02id\x18\x02 \x01(\t\x12\x10\n\x08\x63hild_id\x18\x03 \x03(\t\x12\x11\n\tclient_id\x18\x04 \x01(\t\x12W\n\x13\x61vailability_status\x18\x05 \x01(\x0e\x32:.wireless_android_skyjam.MutateResponse.AvailabilityStatus\x12\x15\n\rerror_message\x18\x06 \x01(\t\"W\n\x12MutateResponseCode\x12\x06\n\x02OK\x10\x01\x12\x0c\n\x08\x43ONFLICT\x10\x02\x12\x13\n\x0fINVALID_REQUEST\x10\x03\x12\x16\n\x12METADATA_TOO_LARGE\x10\x04\"\x86\x01\n\x12\x41vailabilityStatus\x12\x0b\n\x07PENDING\x10\x01\x12\x0b\n\x07MATCHED\x10\x02\x12\x14\n\x10UPLOAD_REQUESTED\x10\x03\x12\r\n\tAVAILABLE\x10\x04\x12\x12\n\x0e\x46ORCE_REUPLOAD\x10\x05\x12\x1d\n\x19UPLOAD_PERMANENTLY_FAILED\x10\x06\"\xe5\x01\n\x18\x42\x61tchMutateTracksRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12\x43\n\x0etrack_mutation\x18\x02 \x03(\x0b\x32+.wireless_android_skyjam.MutateTrackRequest\x12\x1f\n\x11send_notification\x18\x03 \x01(\x08:\x04true\x12\'\n\x19\x64\x65tect_timestamp_conflict\x18\x04 \x01(\x08:\x04true\x12)\n\x1bnotify_fine_grained_updates\x18\x05 \x01(\x08:\x04true\"\xfd\x01\n\x19\x42\x61tchMutateTracksResponse\x12g\n\rresponse_code\x18\x01 \x03(\x0e\x32P.wireless_android_skyjam.BatchMutateTracksResponse.BatchMutateTracksResponseCode\x12@\n\x0fmutate_response\x18\x02 \x03(\x0b\x32\'.wireless_android_skyjam.MutateResponse\"5\n\x1d\x42\x61tchMutateTracksResponseCode\x12\x06\n\x02OK\x10\x01\x12\x0c\n\x08\x43ONFLICT\x10\x02\"\xbf\x02\n\x15MutatePlaylistRequest\x12:\n\x0f\x63reate_playlist\x18\x01 \x01(\x0b\x32!.wireless_android_skyjam.Playlist\x12:\n\x0fupdate_playlist\x18\x02 \x01(\x0b\x32!.wireless_android_skyjam.Playlist\x12\x17\n\x0f\x64\x65lete_playlist\x18\x03 \x01(\t\x12\x16\n\x0epartial_update\x18\x04 \x01(\x08\x12>\n\x0eplaylist_entry\x18\x05 \x03(\x0b\x32&.wireless_android_skyjam.PlaylistEntry\x12\"\n\x14update_last_modified\x18\x06 \x01(\x08:\x04true\x12\x19\n\x11undelete_playlist\x18\x07 \x01(\t\"\xef\x01\n\x1b\x42\x61tchMutatePlaylistsRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12I\n\x11playlist_mutation\x18\x02 \x03(\x0b\x32..wireless_android_skyjam.MutatePlaylistRequest\x12\x1f\n\x11send_notification\x18\x03 \x01(\x08:\x04true\x12\'\n\x19\x64\x65tect_timestamp_conflict\x18\x04 \x01(\x08:\x04true\x12*\n\x1bnotify_fine_grained_updates\x18\x05 \x01(\x08:\x05\x66\x61lse\"\x89\x02\n\x1c\x42\x61tchMutatePlaylistsResponse\x12m\n\rresponse_code\x18\x01 \x03(\x0e\x32V.wireless_android_skyjam.BatchMutatePlaylistsResponse.BatchMutatePlaylistsResponseCode\x12@\n\x0fmutate_response\x18\x02 \x03(\x0b\x32\'.wireless_android_skyjam.MutateResponse\"8\n BatchMutatePlaylistsResponseCode\x12\x06\n\x02OK\x10\x01\x12\x0c\n\x08\x43ONFLICT\x10\x02\"\xb6\x02\n\x1aMutatePlaylistEntryRequest\x12\x45\n\x15\x63reate_playlist_entry\x18\x01 \x01(\x0b\x32&.wireless_android_skyjam.PlaylistEntry\x12\x45\n\x15update_playlist_entry\x18\x02 \x01(\x0b\x32&.wireless_android_skyjam.PlaylistEntry\x12\x45\n\x15\x64\x65lete_playlist_entry\x18\x03 \x01(\x0b\x32&.wireless_android_skyjam.PlaylistEntry\x12\"\n\x14update_last_modified\x18\x04 \x01(\x08:\x04true\x12\x1f\n\x17undelete_playlist_entry\x18\x05 \x01(\t\"\x80\x02\n!BatchMutatePlaylistEntriesRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12T\n\x17playlist_entry_mutation\x18\x02 \x03(\x0b\x32\x33.wireless_android_skyjam.MutatePlaylistEntryRequest\x12\x1f\n\x11send_notification\x18\x03 \x01(\x08:\x04true\x12\'\n\x19\x64\x65tect_timestamp_conflict\x18\x04 \x01(\x08:\x04true\x12*\n\x1bnotify_fine_grained_updates\x18\x05 \x01(\x08:\x05\x66\x61lse\"\xa1\x02\n\"BatchMutatePlaylistEntriesResponse\x12y\n\rresponse_code\x18\x01 \x03(\x0e\x32\x62.wireless_android_skyjam.BatchMutatePlaylistEntriesResponse.BatchMutatePlaylistEntriesResponseCode\x12@\n\x0fmutate_response\x18\x02 \x03(\x0b\x32\'.wireless_android_skyjam.MutateResponse\">\n&BatchMutatePlaylistEntriesResponseCode\x12\x06\n\x02OK\x10\x01\x12\x0c\n\x08\x43ONFLICT\x10\x02\"\xa8\x01\n\x11MagicPlaylistSeed\x12\x46\n\tseed_type\x18\x01 \x02(\x0e\x32\x33.wireless_android_skyjam.MagicPlaylistSeed.SeedType\x12\x0c\n\x04seed\x18\x02 \x02(\t\"=\n\x08SeedType\x12\t\n\x05TRACK\x10\x00\x12\n\n\x06\x41RTIST\x10\x01\x12\t\n\x05\x41LBUM\x10\x02\x12\x0f\n\x0bOPAQUE_SEED\x10\x03\"\xe9\x01\n\x14MagicPlaylistRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12\x15\n\rplaylist_name\x18\x02 \x01(\t\x12\x13\n\x0bplaylist_id\x18\x03 \x01(\t\x12\x38\n\x04seed\x18\x04 \x03(\x0b\x32*.wireless_android_skyjam.MagicPlaylistSeed\x12\x1b\n\x13num_recommendations\x18\x05 \x01(\x05\x12)\n\x1ainclude_all_track_metadata\x18\x06 \x01(\x08:\x05\x66\x61lse\x12\x12\n\nmodel_name\x18\x07 \x01(\t\"\x8c\x01\n\x15MagicPlaylistResponse\x12\x33\n\x08playlist\x18\x01 \x01(\x0b\x32!.wireless_android_skyjam.Playlist\x12>\n\x0eplaylist_entry\x18\x02 \x03(\x0b\x32&.wireless_android_skyjam.PlaylistEntry\"\xf8\x01\n\x12\x46lushLockerRequest\x12\x0f\n\x07gaia_id\x18\x01 \x01(\x03\x12\x13\n\x0bgaia_cookie\x18\x02 \x01(\t\x12#\n\x15remove_audio_binaries\x18\x03 \x01(\x08:\x04true\x12#\n\x15remove_image_binaries\x18\x04 \x01(\x08:\x04true\x12\x1f\n\x11send_notification\x18\x05 \x01(\x08:\x04true\x12&\n\x17reset_subscription_type\x18\x06 \x01(\x08:\x05\x66\x61lse\x12)\n\x1bnotify_fine_grained_updates\x18\x08 \x01(\x08:\x04true\"\x8a\x01\n\x13\x46lushLockerResponse\x12\x16\n\x0etracks_removed\x18\x01 \x01(\x05\x12\x17\n\x0f\x65ntries_removed\x18\x02 \x01(\x05\x12\x19\n\x11playlists_removed\x18\x03 \x01(\x05\x12\'\n\x1fsuccess_reset_subscription_type\x18\x04 \x01(\x08\"6\n\x12LockerNotification\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12\x0f\n\x07payload\x18\x02 \x01(\x0c\"\xee\x01\n\x05\x41lbum\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x14\n\x0c\x61lbum_artist\x18\x02 \x01(\t\x12\x34\n\talbum_art\x18\x03 \x01(\x0b\x32!.wireless_android_skyjam.ImageRef\x12\x13\n\x0btrack_count\x18\x04 \x01(\x05\x12\x18\n\x10last_time_played\x18\x05 \x01(\x03\x12\x16\n\x0eis_compilation\x18\x06 \x01(\x08\x12\x18\n\x10\x61lbum_metajam_id\x18\x07 \x01(\t\x12\x1a\n\x12\x63reation_timestamp\x18\x08 \x01(\x03\x12\x0e\n\x06\x61rtist\x18\t \x01(\t\"\xbb\x01\n\x0e\x41lbumSortOrder\x12I\n\tattribute\x18\x01 \x01(\x0e\x32\x36.wireless_android_skyjam.AlbumSortOrder.AlbumAttribute\x12\x19\n\ndescending\x18\x02 \x01(\x08:\x05\x66\x61lse\"C\n\x0e\x41lbumAttribute\x12\x14\n\x10LAST_PLAYED_TIME\x10\x01\x12\x08\n\x04NAME\x10\x02\x12\x11\n\rCREATION_TIME\x10\x03\"u\n\x10GetAlbumsRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12;\n\nsort_order\x18\x02 \x01(\x0b\x32\'.wireless_android_skyjam.AlbumSortOrder\x12\x13\n\x0bmax_results\x18\x03 \x01(\x05\"B\n\x11GetAlbumsResponse\x12-\n\x05\x61lbum\x18\x01 \x03(\x0b\x32\x1e.wireless_android_skyjam.Album\"`\n\x06\x41rtist\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x19\n\x11total_track_count\x18\x02 \x01(\x05\x12-\n\x05\x61lbum\x18\x03 \x03(\x0b\x32\x1e.wireless_android_skyjam.Album\",\n\x0f\x41rtistSortOrder\x12\x19\n\ndescending\x18\x02 \x01(\x08:\x05\x66\x61lse\"w\n\x11GetArtistsRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12<\n\nsort_order\x18\x02 \x01(\x0b\x32(.wireless_android_skyjam.ArtistSortOrder\x12\x13\n\x0bmax_results\x18\x03 \x01(\x05\"E\n\x12GetArtistsResponse\x12/\n\x06\x61rtist\x18\x01 \x03(\x0b\x32\x1f.wireless_android_skyjam.Artist\"d\n\nMusicGenre\x12\x0c\n\x04name\x18\x01 \x01(\t\x12\x19\n\x11total_track_count\x18\x02 \x01(\x05\x12-\n\x05\x61lbum\x18\x03 \x03(\x0b\x32\x1e.wireless_android_skyjam.Album\"+\n\x0eGenreSortOrder\x12\x19\n\ndescending\x18\x02 \x01(\x08:\x05\x66\x61lse\"u\n\x10GetGenresRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12;\n\nsort_order\x18\x02 \x01(\x0b\x32\'.wireless_android_skyjam.GenreSortOrder\x12\x13\n\x0bmax_results\x18\x03 \x01(\x05\"G\n\x11GetGenresResponse\x12\x32\n\x05genre\x18\x01 \x03(\x0b\x32#.wireless_android_skyjam.MusicGenre\"\xfe\x02\n GetDynamicPlaylistEntriesRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12s\n\x15playlist_entries_type\x18\x04 \x02(\x0e\x32T.wireless_android_skyjam.GetDynamicPlaylistEntriesRequest.DynamicPlaylistEntriesType\x12\x13\n\x0bmax_results\x18\x02 \x01(\x05\x12\x1a\n\x12\x63ontinuation_token\x18\x03 \x01(\t\x12)\n\x1ainclude_all_track_metadata\x18\x05 \x01(\x08:\x05\x66\x61lse\"x\n\x1a\x44ynamicPlaylistEntriesType\x12\r\n\tPURCHASED\x10\x01\x12\r\n\tTHUMBS_UP\x10\x02\x12\x12\n\x0eRECENTLY_ADDED\x10\x03\x12\x0c\n\x08PROMOTED\x10\x04\x12\x1a\n\x16PROMOTED_AND_PURCHASED\x10\x05\"\xa2\x04\n!GetDynamicPlaylistEntriesResponse\x12^\n\rresponse_code\x18\x01 \x02(\x0e\x32G.wireless_android_skyjam.GetDynamicPlaylistEntriesResponse.ResponseCode\x12>\n\x0eplaylist_entry\x18\x02 \x03(\x0b\x32&.wireless_android_skyjam.PlaylistEntry\x12\x1f\n\x17\x65stimated_total_results\x18\x03 \x01(\x03\x12\x1a\n\x12\x63ontinuation_token\x18\x04 \x01(\t\x12t\n\x15playlist_entries_type\x18\x05 \x01(\x0e\x32U.wireless_android_skyjam.GetDynamicPlaylistEntriesResponse.DynamicPlaylistEntriesType\"\x85\x01\n\x1a\x44ynamicPlaylistEntriesType\x12\r\n\tPURCHASED\x10\x01\x12\r\n\tTHUMBS_UP\x10\x02\x12\x12\n\x0eRECENTLY_ADDED\x10\x03\x12\x0c\n\x08PROMOTED\x10\x04\x12\x0b\n\x07UNKNOWN\x10\x05\x12\x1a\n\x16PROMOTED_AND_PURCHASED\x10\x06\"\"\n\x0cResponseCode\x12\x06\n\x02OK\x10\x01\x12\n\n\x06NOT_OK\x10\x02\"4\n!GetAggregationsByTrackTypeRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\"\x82\x02\n\x12TrackTypeAggregate\x12O\n\x10track_type_value\x18\x01 \x01(\x0e\x32\x35.wireless_android_skyjam.TrackTypeAggregate.TrackType\x12\r\n\x05\x63ount\x18\x02 \x01(\x05\"\x8b\x01\n\tTrackType\x12\x11\n\rMATCHED_TRACK\x10\x01\x12\x13\n\x0fUNMATCHED_TRACK\x10\x02\x12\x0f\n\x0bLOCAL_TRACK\x10\x03\x12\x13\n\x0fPURCHASED_TRACK\x10\x04\x12\x1f\n\x1bMETADATA_ONLY_MATCHED_TRACK\x10\x05\x12\x0f\n\x0bPROMO_TRACK\x10\x06\"o\n\"GetAggregationsByTrackTypeResponse\x12I\n\x14track_type_aggregate\x18\x01 \x03(\x0b\x32+.wireless_android_skyjam.TrackTypeAggregate\"=\n*GetAggregationsByAvailabilityStatusRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\"\x9b\x02\n\x1b\x41vailabilityStatusAggregate\x12\x64\n\x13\x61vailability_status\x18\x01 \x01(\x0e\x32G.wireless_android_skyjam.AvailabilityStatusAggregate.AvailabilityStatus\x12\r\n\x05\x63ount\x18\x02 \x01(\x05\"\x86\x01\n\x12\x41vailabilityStatus\x12\x0b\n\x07PENDING\x10\x01\x12\x0b\n\x07MATCHED\x10\x02\x12\x14\n\x10UPLOAD_REQUESTED\x10\x03\x12\r\n\tAVAILABLE\x10\x04\x12\x12\n\x0e\x46ORCE_REUPLOAD\x10\x05\x12\x1d\n\x19UPLOAD_PERMANENTLY_FAILED\x10\x06\"\x8a\x01\n+GetAggregationsByAvailabilityStatusResponse\x12[\n\x1d\x61vailability_status_aggregate\x18\x01 \x03(\x0b\x32\x34.wireless_android_skyjam.AvailabilityStatusAggregate\"7\n\x15\x41\x64\x64PromoTracksRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12\r\n\x05genre\x18\x02 \x03(\t\"G\n\x16\x41\x64\x64PromoTracksResponse\x12-\n\x05track\x18\x01 \x03(\x0b\x32\x1e.wireless_android_skyjam.Track\"J\n\x1eGetPlaylistAggregationsRequest\x12\x0f\n\x07gaia_id\x18\x01 \x02(\x03\x12\x17\n\x0bmax_results\x18\x02 \x01(\x05:\x02\x31\x34\"\x9b\x01\n\x11PlaylistAggregate\x12\x13\n\x0bplaylist_id\x18\x01 \x01(\t\x12\x0c\n\x04name\x18\x02 \x01(\t\x12\x34\n\talbum_art\x18\x03 \x01(\x0b\x32!.wireless_android_skyjam.ImageRef\x12\x13\n\x0btrack_count\x18\x04 \x01(\x03\x12\x18\n\x10last_time_played\x18\x05 \x01(\x03\"i\n\x1fGetPlaylistAggregationsResponse\x12\x46\n\x12playlist_aggregate\x18\x01 \x03(\x0b\x32*.wireless_android_skyjam.PlaylistAggregate\"?\n\x1bRemoteControlCommandRequest\x12\x0f\n\x07gaia_id\x18\x01 \x01(\x03\x12\x0f\n\x07\x63ommand\x18\x02 \x01(\t\"\xcb\x01\n\x1cRemoteControlCommandResponse\x12Y\n\rresponse_code\x18\x01 \x01(\x0e\x32\x42.wireless_android_skyjam.RemoteControlCommandResponse.ResponseCode\"P\n\x0cResponseCode\x12\x06\n\x02OK\x10\x01\x12\x10\n\x0cNO_PUBLISHER\x10\x02\x12\x13\n\x0fINVALID_REQUEST\x10\x03\x12\x11\n\rPUBLISH_ERROR\x10\x04')
,
dependencies=[uits__pb2.DESCRIPTOR,])
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
_AUDIOREF_STORE = _descriptor.EnumDescriptor(
name='Store',
full_name='wireless_android_skyjam.AudioRef.Store',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='BLOBSTORE', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='SM_V2', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=293,
serialized_end=326,
)
_sym_db.RegisterEnumDescriptor(_AUDIOREF_STORE)
_IMAGEREF_STORE = _descriptor.EnumDescriptor(
name='Store',
full_name='wireless_android_skyjam.ImageRef.Store',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='SHOEBOX', index=0, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=531,
serialized_end=551,
)
_sym_db.RegisterEnumDescriptor(_IMAGEREF_STORE)
_IMAGEREF_ORIGIN = _descriptor.EnumDescriptor(
name='Origin',
full_name='wireless_android_skyjam.ImageRef.Origin',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='PERSONAL', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='STORE', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=553,
serialized_end=586,
)
_sym_db.RegisterEnumDescriptor(_IMAGEREF_ORIGIN)
_TRACK_AVAILABILITYSTATUS = _descriptor.EnumDescriptor(
name='AvailabilityStatus',
full_name='wireless_android_skyjam.Track.AvailabilityStatus',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='PENDING', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MATCHED', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UPLOAD_REQUESTED', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='AVAILABLE', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='FORCE_REUPLOAD', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UPLOAD_PERMANENTLY_FAILED', index=5, number=6,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=2761,
serialized_end=2895,
)
_sym_db.RegisterEnumDescriptor(_TRACK_AVAILABILITYSTATUS)
_TRACK_CONTENTTYPE = _descriptor.EnumDescriptor(
name='ContentType',
full_name='wireless_android_skyjam.Track.ContentType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='MP3', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='M4A', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='AAC', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='FLAC', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='OGG', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='WMA', index=5, number=6,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='M4P', index=6, number=7,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ALAC', index=7, number=8,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=2897,
serialized_end=2984,
)
_sym_db.RegisterEnumDescriptor(_TRACK_CONTENTTYPE)
_TRACK_CHANNELS = _descriptor.EnumDescriptor(
name='Channels',
full_name='wireless_android_skyjam.Track.Channels',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='MONO', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='STEREO', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=2986,
serialized_end=3018,
)
_sym_db.RegisterEnumDescriptor(_TRACK_CHANNELS)
_TRACK_TRACKTYPE = _descriptor.EnumDescriptor(
name='TrackType',
full_name='wireless_android_skyjam.Track.TrackType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='MATCHED_TRACK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UNMATCHED_TRACK', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='LOCAL_TRACK', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PURCHASED_TRACK', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='METADATA_ONLY_MATCHED_TRACK', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PROMO_TRACK', index=5, number=6,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=3021,
serialized_end=3160,
)
_sym_db.RegisterEnumDescriptor(_TRACK_TRACKTYPE)
_TRACK_RATING = _descriptor.EnumDescriptor(
name='Rating',
full_name='wireless_android_skyjam.Track.Rating',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='NOT_RATED', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ONE_STAR', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='TWO_STARS', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='THREE_STARS', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='FOUR_STARS', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='FIVE_STARS', index=5, number=6,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=3162,
serialized_end=3263,
)
_sym_db.RegisterEnumDescriptor(_TRACK_RATING)
_TRACK_EXPLICITTYPE = _descriptor.EnumDescriptor(
name='ExplicitType',
full_name='wireless_android_skyjam.Track.ExplicitType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='EXPLICIT', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLEAN', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='EDITED', index=2, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=3265,
serialized_end=3316,
)
_sym_db.RegisterEnumDescriptor(_TRACK_EXPLICITTYPE)
_PLAYLIST_PLAYLISTTYPE = _descriptor.EnumDescriptor(
name='PlaylistType',
full_name='wireless_android_skyjam.Playlist.PlaylistType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='USER_GENERATED', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MAGIC', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PROMO', index=2, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=3676,
serialized_end=3732,
)
_sym_db.RegisterEnumDescriptor(_PLAYLIST_PLAYLISTTYPE)
_PLAYLISTENTRY_RELATIVEPOSITIONIDTYPE = _descriptor.EnumDescriptor(
name='RelativePositionIdType',
full_name='wireless_android_skyjam.PlaylistEntry.RelativePositionIdType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='SERVER', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=4165,
serialized_end=4213,
)
_sym_db.RegisterEnumDescriptor(_PLAYLISTENTRY_RELATIVEPOSITIONIDTYPE)
_TRACKSEARCHRESTRICTION_TRACKATTRIBUTE = _descriptor.EnumDescriptor(
name='TrackAttribute',
full_name='wireless_android_skyjam.TrackSearchRestriction.TrackAttribute',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='TITLE', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ARTIST', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ALBUM', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ALBUM_ARTIST', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='GENRE', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='AVAILABILITY_STATUS', index=5, number=6,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='TRACK_TYPE', index=6, number=7,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='YEAR', index=7, number=8,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='STORE_ID', index=8, number=9,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ALBUM_METAJAM_ID', index=9, number=10,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=4430,
serialized_end=4596,
)
_sym_db.RegisterEnumDescriptor(_TRACKSEARCHRESTRICTION_TRACKATTRIBUTE)
_TRACKSEARCHRESTRICTION_COMPARISONTYPE = _descriptor.EnumDescriptor(
name='ComparisonType',
full_name='wireless_android_skyjam.TrackSearchRestriction.ComparisonType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='EQUAL', index=0, number=0,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='NOT_EQUAL', index=1, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='GREATER_THAN', index=2, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='GREATER_EQUAL', index=3, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='LESS_THAN', index=4, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='LESS_EQUAL', index=5, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PARTIAL_MATCH', index=6, number=6,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=4599,
serialized_end=4728,
)
_sym_db.RegisterEnumDescriptor(_TRACKSEARCHRESTRICTION_COMPARISONTYPE)
_TRACKSEARCHRESTRICTIONSET_RESTRICTIONSETTYPE = _descriptor.EnumDescriptor(
name='RestrictionSetType',
full_name='wireless_android_skyjam.TrackSearchRestrictionSet.RestrictionSetType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='AND', index=0, number=0,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='OR', index=1, number=1,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=4984,
serialized_end=5021,
)
_sym_db.RegisterEnumDescriptor(_TRACKSEARCHRESTRICTIONSET_RESTRICTIONSETTYPE)
_TRACKSORTORDER_TRACKATTRIBUTE = _descriptor.EnumDescriptor(
name='TrackAttribute',
full_name='wireless_android_skyjam.TrackSortOrder.TrackAttribute',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='LAST_MODIFIED_TIME', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ARTIST', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ALBUM', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='TITLE', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='TRACK_NUMBER', index=4, number=6,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PLAY_COUNT', index=5, number=9,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='DURATION_MILLIS', index=6, number=10,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='RATING', index=7, number=11,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CREATION_TIME', index=8, number=12,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=5144,
serialized_end=5304,
)
_sym_db.RegisterEnumDescriptor(_TRACKSORTORDER_TRACKATTRIBUTE)
_GETTRACKSREQUEST_TRACKPROJECTION = _descriptor.EnumDescriptor(
name='TrackProjection',
full_name='wireless_android_skyjam.GetTracksRequest.TrackProjection',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='FULL', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='FRONTEND_VIEW', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=5739,
serialized_end=5785,
)
_sym_db.RegisterEnumDescriptor(_GETTRACKSREQUEST_TRACKPROJECTION)
_GETTRACKSRESPONSE_RESPONSECODE = _descriptor.EnumDescriptor(
name='ResponseCode',
full_name='wireless_android_skyjam.GetTracksResponse.ResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='NOT_MODIFIED', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='GONE', index=2, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=5997,
serialized_end=6047,
)
_sym_db.RegisterEnumDescriptor(_GETTRACKSRESPONSE_RESPONSECODE)
_GETPLAYLISTENTRIESRESPONSE_RESPONSECODE = _descriptor.EnumDescriptor(
name='ResponseCode',
full_name='wireless_android_skyjam.GetPlaylistEntriesResponse.ResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='NOT_MODIFIED', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='GONE', index=2, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=5997,
serialized_end=6047,
)
_sym_db.RegisterEnumDescriptor(_GETPLAYLISTENTRIESRESPONSE_RESPONSECODE)
_PLAYLISTSORTORDER_PLAYLISTATTRIBUTE = _descriptor.EnumDescriptor(
name='PlaylistAttribute',
full_name='wireless_android_skyjam.PlaylistSortOrder.PlaylistAttribute',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='LAST_MODIFIED_TIME', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='TITLE', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CREATION_TIME', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='RECENT_TIMESTAMP', index=3, number=4,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=6731,
serialized_end=6826,
)
_sym_db.RegisterEnumDescriptor(_PLAYLISTSORTORDER_PLAYLISTATTRIBUTE)
_GETPLAYLISTSRESPONSE_RESPONSECODE = _descriptor.EnumDescriptor(
name='ResponseCode',
full_name='wireless_android_skyjam.GetPlaylistsResponse.ResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='NOT_MODIFIED', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='GONE', index=2, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=5997,
serialized_end=6047,
)
_sym_db.RegisterEnumDescriptor(_GETPLAYLISTSRESPONSE_RESPONSECODE)
_BATCHLOOKUPREQUEST_METADATATYPE = _descriptor.EnumDescriptor(
name='MetadataType',
full_name='wireless_android_skyjam.BatchLookupRequest.MetadataType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='TRACK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PLAYLIST', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PLAYLIST_ENTRY', index=2, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=7828,
serialized_end=7887,
)
_sym_db.RegisterEnumDescriptor(_BATCHLOOKUPREQUEST_METADATATYPE)
_MUTATERESPONSE_MUTATERESPONSECODE = _descriptor.EnumDescriptor(
name='MutateResponseCode',
full_name='wireless_android_skyjam.MutateResponse.MutateResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CONFLICT', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='INVALID_REQUEST', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='METADATA_TOO_LARGE', index=3, number=4,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=8577,
serialized_end=8664,
)
_sym_db.RegisterEnumDescriptor(_MUTATERESPONSE_MUTATERESPONSECODE)
_MUTATERESPONSE_AVAILABILITYSTATUS = _descriptor.EnumDescriptor(
name='AvailabilityStatus',
full_name='wireless_android_skyjam.MutateResponse.AvailabilityStatus',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='PENDING', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MATCHED', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UPLOAD_REQUESTED', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='AVAILABLE', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='FORCE_REUPLOAD', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UPLOAD_PERMANENTLY_FAILED', index=5, number=6,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=2761,
serialized_end=2895,
)
_sym_db.RegisterEnumDescriptor(_MUTATERESPONSE_AVAILABILITYSTATUS)
_BATCHMUTATETRACKSRESPONSE_BATCHMUTATETRACKSRESPONSECODE = _descriptor.EnumDescriptor(
name='BatchMutateTracksResponseCode',
full_name='wireless_android_skyjam.BatchMutateTracksResponse.BatchMutateTracksResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CONFLICT', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=9236,
serialized_end=9289,
)
_sym_db.RegisterEnumDescriptor(_BATCHMUTATETRACKSRESPONSE_BATCHMUTATETRACKSRESPONSECODE)
_BATCHMUTATEPLAYLISTSRESPONSE_BATCHMUTATEPLAYLISTSRESPONSECODE = _descriptor.EnumDescriptor(
name='BatchMutatePlaylistsResponseCode',
full_name='wireless_android_skyjam.BatchMutatePlaylistsResponse.BatchMutatePlaylistsResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CONFLICT', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=10065,
serialized_end=10121,
)
_sym_db.RegisterEnumDescriptor(_BATCHMUTATEPLAYLISTSRESPONSE_BATCHMUTATEPLAYLISTSRESPONSECODE)
_BATCHMUTATEPLAYLISTENTRIESRESPONSE_BATCHMUTATEPLAYLISTENTRIESRESPONSECODE = _descriptor.EnumDescriptor(
name='BatchMutatePlaylistEntriesResponseCode',
full_name='wireless_android_skyjam.BatchMutatePlaylistEntriesResponse.BatchMutatePlaylistEntriesResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CONFLICT', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=10923,
serialized_end=10985,
)
_sym_db.RegisterEnumDescriptor(_BATCHMUTATEPLAYLISTENTRIESRESPONSE_BATCHMUTATEPLAYLISTENTRIESRESPONSECODE)
_MAGICPLAYLISTSEED_SEEDTYPE = _descriptor.EnumDescriptor(
name='SeedType',
full_name='wireless_android_skyjam.MagicPlaylistSeed.SeedType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='TRACK', index=0, number=0,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ARTIST', index=1, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ALBUM', index=2, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='OPAQUE_SEED', index=3, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=11095,
serialized_end=11156,
)
_sym_db.RegisterEnumDescriptor(_MAGICPLAYLISTSEED_SEEDTYPE)
_ALBUMSORTORDER_ALBUMATTRIBUTE = _descriptor.EnumDescriptor(
name='AlbumAttribute',
full_name='wireless_android_skyjam.AlbumSortOrder.AlbumAttribute',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='LAST_PLAYED_TIME', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='NAME', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CREATION_TIME', index=2, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=12347,
serialized_end=12414,
)
_sym_db.RegisterEnumDescriptor(_ALBUMSORTORDER_ALBUMATTRIBUTE)
_GETDYNAMICPLAYLISTENTRIESREQUEST_DYNAMICPLAYLISTENTRIESTYPE = _descriptor.EnumDescriptor(
name='DynamicPlaylistEntriesType',
full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesRequest.DynamicPlaylistEntriesType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='PURCHASED', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='THUMBS_UP', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='RECENTLY_ADDED', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PROMOTED', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PROMOTED_AND_PURCHASED', index=4, number=5,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=13541,
serialized_end=13661,
)
_sym_db.RegisterEnumDescriptor(_GETDYNAMICPLAYLISTENTRIESREQUEST_DYNAMICPLAYLISTENTRIESTYPE)
_GETDYNAMICPLAYLISTENTRIESRESPONSE_DYNAMICPLAYLISTENTRIESTYPE = _descriptor.EnumDescriptor(
name='DynamicPlaylistEntriesType',
full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesResponse.DynamicPlaylistEntriesType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='PURCHASED', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='THUMBS_UP', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='RECENTLY_ADDED', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PROMOTED', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UNKNOWN', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PROMOTED_AND_PURCHASED', index=5, number=6,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=14041,
serialized_end=14174,
)
_sym_db.RegisterEnumDescriptor(_GETDYNAMICPLAYLISTENTRIESRESPONSE_DYNAMICPLAYLISTENTRIESTYPE)
_GETDYNAMICPLAYLISTENTRIESRESPONSE_RESPONSECODE = _descriptor.EnumDescriptor(
name='ResponseCode',
full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesResponse.ResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='NOT_OK', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=14176,
serialized_end=14210,
)
_sym_db.RegisterEnumDescriptor(_GETDYNAMICPLAYLISTENTRIESRESPONSE_RESPONSECODE)
_TRACKTYPEAGGREGATE_TRACKTYPE = _descriptor.EnumDescriptor(
name='TrackType',
full_name='wireless_android_skyjam.TrackTypeAggregate.TrackType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='MATCHED_TRACK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UNMATCHED_TRACK', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='LOCAL_TRACK', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PURCHASED_TRACK', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='METADATA_ONLY_MATCHED_TRACK', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PROMO_TRACK', index=5, number=6,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=3021,
serialized_end=3160,
)
_sym_db.RegisterEnumDescriptor(_TRACKTYPEAGGREGATE_TRACKTYPE)
_AVAILABILITYSTATUSAGGREGATE_AVAILABILITYSTATUS = _descriptor.EnumDescriptor(
name='AvailabilityStatus',
full_name='wireless_android_skyjam.AvailabilityStatusAggregate.AvailabilityStatus',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='PENDING', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MATCHED', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UPLOAD_REQUESTED', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='AVAILABLE', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='FORCE_REUPLOAD', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UPLOAD_PERMANENTLY_FAILED', index=5, number=6,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=2761,
serialized_end=2895,
)
_sym_db.RegisterEnumDescriptor(_AVAILABILITYSTATUSAGGREGATE_AVAILABILITYSTATUS)
_REMOTECONTROLCOMMANDRESPONSE_RESPONSECODE = _descriptor.EnumDescriptor(
name='ResponseCode',
full_name='wireless_android_skyjam.RemoteControlCommandResponse.ResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='NO_PUBLISHER', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='INVALID_REQUEST', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PUBLISH_ERROR', index=3, number=4,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=15790,
serialized_end=15870,
)
_sym_db.RegisterEnumDescriptor(_REMOTECONTROLCOMMANDRESPONSE_RESPONSECODE)
_AUDIOREF = _descriptor.Descriptor(
name='AudioRef',
full_name='wireless_android_skyjam.AudioRef',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='store', full_name='wireless_android_skyjam.AudioRef.store', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='ref', full_name='wireless_android_skyjam.AudioRef.ref', index=1,
number=2, type=12, cpp_type=9, label=2,
has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='url', full_name='wireless_android_skyjam.AudioRef.url', index=2,
number=4, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='bit_rate', full_name='wireless_android_skyjam.AudioRef.bit_rate', index=3,
number=5, 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,
options=None),
_descriptor.FieldDescriptor(
name='sample_rate', full_name='wireless_android_skyjam.AudioRef.sample_rate', index=4,
number=6, 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,
options=None),
_descriptor.FieldDescriptor(
name='downloadable', full_name='wireless_android_skyjam.AudioRef.downloadable', index=5,
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,
options=None),
_descriptor.FieldDescriptor(
name='duration_millis', full_name='wireless_android_skyjam.AudioRef.duration_millis', index=6,
number=8, 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,
options=None),
_descriptor.FieldDescriptor(
name='rematch_timestamp', full_name='wireless_android_skyjam.AudioRef.rematch_timestamp', index=7,
number=9, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='invalid_due_to_wipeout', full_name='wireless_android_skyjam.AudioRef.invalid_due_to_wipeout', index=8,
number=10, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_AUDIOREF_STORE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=54,
serialized_end=326,
)
_IMAGEREF = _descriptor.Descriptor(
name='ImageRef',
full_name='wireless_android_skyjam.ImageRef',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='store', full_name='wireless_android_skyjam.ImageRef.store', index=0,
number=1, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=3,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='width', full_name='wireless_android_skyjam.ImageRef.width', index=1,
number=2, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='height', full_name='wireless_android_skyjam.ImageRef.height', index=2,
number=3, type=13, cpp_type=3, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='url', full_name='wireless_android_skyjam.ImageRef.url', index=3,
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,
options=None),
_descriptor.FieldDescriptor(
name='invalid_due_to_wipeout', full_name='wireless_android_skyjam.ImageRef.invalid_due_to_wipeout', index=4,
number=7, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='origin', full_name='wireless_android_skyjam.ImageRef.origin', index=5,
number=8, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_IMAGEREF_STORE,
_IMAGEREF_ORIGIN,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=329,
serialized_end=586,
)
_UPLOADEDUITSID3TAG = _descriptor.Descriptor(
name='UploadedUitsId3Tag',
full_name='wireless_android_skyjam.UploadedUitsId3Tag',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='owner', full_name='wireless_android_skyjam.UploadedUitsId3Tag.owner', 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,
options=None),
_descriptor.FieldDescriptor(
name='data', full_name='wireless_android_skyjam.UploadedUitsId3Tag.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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=588,
serialized_end=637,
)
_ADDITIONALMETADATA = _descriptor.Descriptor(
name='AdditionalMetadata',
full_name='wireless_android_skyjam.AdditionalMetadata',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='tag_name', full_name='wireless_android_skyjam.AdditionalMetadata.tag_name', 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,
options=None),
_descriptor.FieldDescriptor(
name='value', full_name='wireless_android_skyjam.AdditionalMetadata.value', index=1,
number=2, type=12, cpp_type=9, label=2,
has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=639,
serialized_end=692,
)
_TRACKEXTRAS = _descriptor.Descriptor(
name='TrackExtras',
full_name='wireless_android_skyjam.TrackExtras',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='additional_metadata', full_name='wireless_android_skyjam.TrackExtras.additional_metadata', index=0,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='candidates', full_name='wireless_android_skyjam.TrackExtras.candidates', index=1,
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,
options=None),
_descriptor.FieldDescriptor(
name='original_sample_rate', full_name='wireless_android_skyjam.TrackExtras.original_sample_rate', index=2,
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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=695,
serialized_end=832,
)
_TRACK = _descriptor.Descriptor(
name='Track',
full_name='wireless_android_skyjam.Track',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.Track.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,
options=None),
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.Track.client_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,
options=None),
_descriptor.FieldDescriptor(
name='creation_timestamp', full_name='wireless_android_skyjam.Track.creation_timestamp', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='last_modified_timestamp', full_name='wireless_android_skyjam.Track.last_modified_timestamp', index=3,
number=4, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='deleted', full_name='wireless_android_skyjam.Track.deleted', index=4,
number=5, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='title', full_name='wireless_android_skyjam.Track.title', 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,
options=None),
_descriptor.FieldDescriptor(
name='artist', full_name='wireless_android_skyjam.Track.artist', index=6,
number=7, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='artist_hash', full_name='wireless_android_skyjam.Track.artist_hash', index=7,
number=46, 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,
options=None),
_descriptor.FieldDescriptor(
name='composer', full_name='wireless_android_skyjam.Track.composer', index=8,
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,
options=None),
_descriptor.FieldDescriptor(
name='album', full_name='wireless_android_skyjam.Track.album', index=9,
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,
options=None),
_descriptor.FieldDescriptor(
name='album_artist', full_name='wireless_android_skyjam.Track.album_artist', index=10,
number=10, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='canonical_album', full_name='wireless_android_skyjam.Track.canonical_album', index=11,
number=56, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='canonical_artist', full_name='wireless_android_skyjam.Track.canonical_artist', index=12,
number=57, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='canonical_genre_album', full_name='wireless_android_skyjam.Track.canonical_genre_album', index=13,
number=58, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='year', full_name='wireless_android_skyjam.Track.year', index=14,
number=11, 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,
options=None),
_descriptor.FieldDescriptor(
name='comment', full_name='wireless_android_skyjam.Track.comment', index=15,
number=12, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='track_number', full_name='wireless_android_skyjam.Track.track_number', index=16,
number=13, 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,
options=None),
_descriptor.FieldDescriptor(
name='genre', full_name='wireless_android_skyjam.Track.genre', index=17,
number=14, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='duration_millis', full_name='wireless_android_skyjam.Track.duration_millis', index=18,
number=15, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='beats_per_minute', full_name='wireless_android_skyjam.Track.beats_per_minute', index=19,
number=16, 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,
options=None),
_descriptor.FieldDescriptor(
name='original_bit_rate', full_name='wireless_android_skyjam.Track.original_bit_rate', index=20,
number=44, 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,
options=None),
_descriptor.FieldDescriptor(
name='audio_ref', full_name='wireless_android_skyjam.Track.audio_ref', index=21,
number=17, 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,
options=None),
_descriptor.FieldDescriptor(
name='album_art_ref', full_name='wireless_android_skyjam.Track.album_art_ref', index=22,
number=18, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='availability_status', full_name='wireless_android_skyjam.Track.availability_status', index=23,
number=19, 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,
options=None),
_descriptor.FieldDescriptor(
name='play_count', full_name='wireless_android_skyjam.Track.play_count', index=24,
number=20, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='content_type', full_name='wireless_android_skyjam.Track.content_type', index=25,
number=25, 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,
options=None),
_descriptor.FieldDescriptor(
name='total_track_count', full_name='wireless_android_skyjam.Track.total_track_count', index=26,
number=26, 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,
options=None),
_descriptor.FieldDescriptor(
name='disc_number', full_name='wireless_android_skyjam.Track.disc_number', index=27,
number=27, 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,
options=None),
_descriptor.FieldDescriptor(
name='total_disc_count', full_name='wireless_android_skyjam.Track.total_disc_count', index=28,
number=28, 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,
options=None),
_descriptor.FieldDescriptor(
name='channels', full_name='wireless_android_skyjam.Track.channels', index=29,
number=29, 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,
options=None),
_descriptor.FieldDescriptor(
name='track_type', full_name='wireless_android_skyjam.Track.track_type', index=30,
number=30, 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,
options=None),
_descriptor.FieldDescriptor(
name='use_single_server_copy', full_name='wireless_android_skyjam.Track.use_single_server_copy', index=31,
number=59, 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,
options=None),
_descriptor.FieldDescriptor(
name='rating', full_name='wireless_android_skyjam.Track.rating', index=32,
number=31, 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,
options=None),
_descriptor.FieldDescriptor(
name='estimated_size', full_name='wireless_android_skyjam.Track.estimated_size', index=33,
number=32, 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,
options=None),
_descriptor.FieldDescriptor(
name='store_id', full_name='wireless_android_skyjam.Track.store_id', index=34,
number=33, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='metajam_id', full_name='wireless_android_skyjam.Track.metajam_id', index=35,
number=34, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='metajam_id_confidence', full_name='wireless_android_skyjam.Track.metajam_id_confidence', index=36,
number=43, type=1, cpp_type=5, label=1,
has_default_value=True, default_value=float(0),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='uits', full_name='wireless_android_skyjam.Track.uits', index=37,
number=35, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='uits_metadata', full_name='wireless_android_skyjam.Track.uits_metadata', index=38,
number=40, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='compilation', full_name='wireless_android_skyjam.Track.compilation', index=39,
number=36, 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,
options=None),
_descriptor.FieldDescriptor(
name='client_date_added', full_name='wireless_android_skyjam.Track.client_date_added', index=40,
number=37, 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,
options=None),
_descriptor.FieldDescriptor(
name='recent_timestamp', full_name='wireless_android_skyjam.Track.recent_timestamp', index=41,
number=38, 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,
options=None),
_descriptor.FieldDescriptor(
name='do_not_rematch', full_name='wireless_android_skyjam.Track.do_not_rematch', index=42,
number=39, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='from_album_purchase', full_name='wireless_android_skyjam.Track.from_album_purchase', index=43,
number=41, 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,
options=None),
_descriptor.FieldDescriptor(
name='album_metajam_id', full_name='wireless_android_skyjam.Track.album_metajam_id', index=44,
number=42, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='transaction_id', full_name='wireless_android_skyjam.Track.transaction_id', index=45,
number=45, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='debug_track', full_name='wireless_android_skyjam.Track.debug_track', index=46,
number=47, 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,
options=None),
_descriptor.FieldDescriptor(
name='normalized_title', full_name='wireless_android_skyjam.Track.normalized_title', index=47,
number=48, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='normalized_artist', full_name='wireless_android_skyjam.Track.normalized_artist', index=48,
number=49, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='normalized_album', full_name='wireless_android_skyjam.Track.normalized_album', index=49,
number=50, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='normalized_album_artist', full_name='wireless_android_skyjam.Track.normalized_album_artist', index=50,
number=51, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='normalized_canonical_album', full_name='wireless_android_skyjam.Track.normalized_canonical_album', index=51,
number=54, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='normalized_canonical_artist', full_name='wireless_android_skyjam.Track.normalized_canonical_artist', index=52,
number=55, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.Track.uploader_id', index=53,
number=52, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='client_album_id', full_name='wireless_android_skyjam.Track.client_album_id', index=54,
number=53, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='label_owner_code', full_name='wireless_android_skyjam.Track.label_owner_code', index=55,
number=60, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='original_content_type', full_name='wireless_android_skyjam.Track.original_content_type', index=56,
number=61, 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,
options=None),
_descriptor.FieldDescriptor(
name='uploaded_uits', full_name='wireless_android_skyjam.Track.uploaded_uits', index=57,
number=71, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='explicit_type', full_name='wireless_android_skyjam.Track.explicit_type', index=58,
number=74, 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,
options=None),
_descriptor.FieldDescriptor(
name='track_extras', full_name='wireless_android_skyjam.Track.track_extras', index=59,
number=75, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_TRACK_AVAILABILITYSTATUS,
_TRACK_CONTENTTYPE,
_TRACK_CHANNELS,
_TRACK_TRACKTYPE,
_TRACK_RATING,
_TRACK_EXPLICITTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=835,
serialized_end=3316,
)
_TRACKS = _descriptor.Descriptor(
name='Tracks',
full_name='wireless_android_skyjam.Tracks',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='track', full_name='wireless_android_skyjam.Tracks.track', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=3318,
serialized_end=3373,
)
_PLAYLIST = _descriptor.Descriptor(
name='Playlist',
full_name='wireless_android_skyjam.Playlist',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.Playlist.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,
options=None),
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.Playlist.client_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,
options=None),
_descriptor.FieldDescriptor(
name='creation_timestamp', full_name='wireless_android_skyjam.Playlist.creation_timestamp', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='last_modified_timestamp', full_name='wireless_android_skyjam.Playlist.last_modified_timestamp', index=3,
number=4, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='deleted', full_name='wireless_android_skyjam.Playlist.deleted', index=4,
number=5, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='name', full_name='wireless_android_skyjam.Playlist.name', 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,
options=None),
_descriptor.FieldDescriptor(
name='playlist_type', full_name='wireless_android_skyjam.Playlist.playlist_type', index=6,
number=7, 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,
options=None),
_descriptor.FieldDescriptor(
name='playlist_art_ref', full_name='wireless_android_skyjam.Playlist.playlist_art_ref', index=7,
number=8, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='recent_timestamp', full_name='wireless_android_skyjam.Playlist.recent_timestamp', index=8,
number=9, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_PLAYLIST_PLAYLISTTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=3376,
serialized_end=3732,
)
_PLAYLISTENTRY = _descriptor.Descriptor(
name='PlaylistEntry',
full_name='wireless_android_skyjam.PlaylistEntry',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='playlist_id', full_name='wireless_android_skyjam.PlaylistEntry.playlist_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,
options=None),
_descriptor.FieldDescriptor(
name='absolute_position', full_name='wireless_android_skyjam.PlaylistEntry.absolute_position', 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,
options=None),
_descriptor.FieldDescriptor(
name='place_after_entry_id', full_name='wireless_android_skyjam.PlaylistEntry.place_after_entry_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,
options=None),
_descriptor.FieldDescriptor(
name='track_id', full_name='wireless_android_skyjam.PlaylistEntry.track_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.PlaylistEntry.id', 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,
options=None),
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.PlaylistEntry.client_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='creation_timestamp', full_name='wireless_android_skyjam.PlaylistEntry.creation_timestamp', index=6,
number=7, 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,
options=None),
_descriptor.FieldDescriptor(
name='last_modified_timestamp', full_name='wireless_android_skyjam.PlaylistEntry.last_modified_timestamp', index=7,
number=8, 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,
options=None),
_descriptor.FieldDescriptor(
name='deleted', full_name='wireless_android_skyjam.PlaylistEntry.deleted', index=8,
number=9, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='relative_position_id_type', full_name='wireless_android_skyjam.PlaylistEntry.relative_position_id_type', index=9,
number=10, 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,
options=None),
_descriptor.FieldDescriptor(
name='track', full_name='wireless_android_skyjam.PlaylistEntry.track', index=10,
number=15, 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,
options=None),
_descriptor.FieldDescriptor(
name='place_before_entry_id', full_name='wireless_android_skyjam.PlaylistEntry.place_before_entry_id', index=11,
number=16, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='string_position', full_name='wireless_android_skyjam.PlaylistEntry.string_position', index=12,
number=17, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_PLAYLISTENTRY_RELATIVEPOSITIONIDTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=3735,
serialized_end=4213,
)
_TRACKSEARCHRESTRICTION = _descriptor.Descriptor(
name='TrackSearchRestriction',
full_name='wireless_android_skyjam.TrackSearchRestriction',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='attribute', full_name='wireless_android_skyjam.TrackSearchRestriction.attribute', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='value', full_name='wireless_android_skyjam.TrackSearchRestriction.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,
options=None),
_descriptor.FieldDescriptor(
name='comparison_type', full_name='wireless_android_skyjam.TrackSearchRestriction.comparison_type', index=2,
number=3, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_TRACKSEARCHRESTRICTION_TRACKATTRIBUTE,
_TRACKSEARCHRESTRICTION_COMPARISONTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=4216,
serialized_end=4728,
)
_TRACKSEARCHRESTRICTIONSET = _descriptor.Descriptor(
name='TrackSearchRestrictionSet',
full_name='wireless_android_skyjam.TrackSearchRestrictionSet',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='type', full_name='wireless_android_skyjam.TrackSearchRestrictionSet.type', index=0,
number=1, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='restriction', full_name='wireless_android_skyjam.TrackSearchRestrictionSet.restriction', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='sub_set', full_name='wireless_android_skyjam.TrackSearchRestrictionSet.sub_set', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_TRACKSEARCHRESTRICTIONSET_RESTRICTIONSETTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=4731,
serialized_end=5021,
)
_TRACKSORTORDER = _descriptor.Descriptor(
name='TrackSortOrder',
full_name='wireless_android_skyjam.TrackSortOrder',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='attribute', full_name='wireless_android_skyjam.TrackSortOrder.attribute', index=0,
number=1, 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,
options=None),
_descriptor.FieldDescriptor(
name='descending', full_name='wireless_android_skyjam.TrackSortOrder.descending', index=1,
number=2, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_TRACKSORTORDER_TRACKATTRIBUTE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5024,
serialized_end=5304,
)
_GETTRACKSREQUEST = _descriptor.Descriptor(
name='GetTracksRequest',
full_name='wireless_android_skyjam.GetTracksRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.GetTracksRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='updated_min', full_name='wireless_android_skyjam.GetTracksRequest.updated_min', 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,
options=None),
_descriptor.FieldDescriptor(
name='include_deleted', full_name='wireless_android_skyjam.GetTracksRequest.include_deleted', 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,
options=None),
_descriptor.FieldDescriptor(
name='max_results', full_name='wireless_android_skyjam.GetTracksRequest.max_results', 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,
options=None),
_descriptor.FieldDescriptor(
name='continuation_token', full_name='wireless_android_skyjam.GetTracksRequest.continuation_token', 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,
options=None),
_descriptor.FieldDescriptor(
name='search_restriction', full_name='wireless_android_skyjam.GetTracksRequest.search_restriction', index=5,
number=6, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='sort_order', full_name='wireless_android_skyjam.GetTracksRequest.sort_order', index=6,
number=7, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='restriction_set', full_name='wireless_android_skyjam.GetTracksRequest.restriction_set', index=7,
number=8, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='track_projection', full_name='wireless_android_skyjam.GetTracksRequest.track_projection', index=8,
number=9, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_GETTRACKSREQUEST_TRACKPROJECTION,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5307,
serialized_end=5785,
)
_GETTRACKSRESPONSE = _descriptor.Descriptor(
name='GetTracksResponse',
full_name='wireless_android_skyjam.GetTracksResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.GetTracksResponse.response_code', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='track', full_name='wireless_android_skyjam.GetTracksResponse.track', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='estimated_total_results', full_name='wireless_android_skyjam.GetTracksResponse.estimated_total_results', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='continuation_token', full_name='wireless_android_skyjam.GetTracksResponse.continuation_token', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_GETTRACKSRESPONSE_RESPONSECODE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5788,
serialized_end=6047,
)
_GETPLAYLISTENTRIESREQUEST = _descriptor.Descriptor(
name='GetPlaylistEntriesRequest',
full_name='wireless_android_skyjam.GetPlaylistEntriesRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.GetPlaylistEntriesRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='updated_min', full_name='wireless_android_skyjam.GetPlaylistEntriesRequest.updated_min', 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,
options=None),
_descriptor.FieldDescriptor(
name='include_deleted', full_name='wireless_android_skyjam.GetPlaylistEntriesRequest.include_deleted', 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,
options=None),
_descriptor.FieldDescriptor(
name='max_results', full_name='wireless_android_skyjam.GetPlaylistEntriesRequest.max_results', 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,
options=None),
_descriptor.FieldDescriptor(
name='continuation_token', full_name='wireless_android_skyjam.GetPlaylistEntriesRequest.continuation_token', 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,
options=None),
_descriptor.FieldDescriptor(
name='playlist_id_filter', full_name='wireless_android_skyjam.GetPlaylistEntriesRequest.playlist_id_filter', 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,
options=None),
_descriptor.FieldDescriptor(
name='include_all_track_metadata', full_name='wireless_android_skyjam.GetPlaylistEntriesRequest.include_all_track_metadata', index=6,
number=7, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='only_show_available_tracks', full_name='wireless_android_skyjam.GetPlaylistEntriesRequest.only_show_available_tracks', index=7,
number=8, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=6050,
serialized_end=6302,
)
_GETPLAYLISTENTRIESRESPONSE = _descriptor.Descriptor(
name='GetPlaylistEntriesResponse',
full_name='wireless_android_skyjam.GetPlaylistEntriesResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.GetPlaylistEntriesResponse.response_code', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entry', full_name='wireless_android_skyjam.GetPlaylistEntriesResponse.playlist_entry', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='estimated_total_results', full_name='wireless_android_skyjam.GetPlaylistEntriesResponse.estimated_total_results', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='continuation_token', full_name='wireless_android_skyjam.GetPlaylistEntriesResponse.continuation_token', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_GETPLAYLISTENTRIESRESPONSE_RESPONSECODE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=6305,
serialized_end=6599,
)
_PLAYLISTSORTORDER = _descriptor.Descriptor(
name='PlaylistSortOrder',
full_name='wireless_android_skyjam.PlaylistSortOrder',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='attribute', full_name='wireless_android_skyjam.PlaylistSortOrder.attribute', index=0,
number=1, 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,
options=None),
_descriptor.FieldDescriptor(
name='descending', full_name='wireless_android_skyjam.PlaylistSortOrder.descending', index=1,
number=2, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_PLAYLISTSORTORDER_PLAYLISTATTRIBUTE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=6602,
serialized_end=6826,
)
_GETPLAYLISTSREQUEST = _descriptor.Descriptor(
name='GetPlaylistsRequest',
full_name='wireless_android_skyjam.GetPlaylistsRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.GetPlaylistsRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='updated_min', full_name='wireless_android_skyjam.GetPlaylistsRequest.updated_min', 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,
options=None),
_descriptor.FieldDescriptor(
name='include_deleted', full_name='wireless_android_skyjam.GetPlaylistsRequest.include_deleted', 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,
options=None),
_descriptor.FieldDescriptor(
name='max_results', full_name='wireless_android_skyjam.GetPlaylistsRequest.max_results', 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,
options=None),
_descriptor.FieldDescriptor(
name='continuation_token', full_name='wireless_android_skyjam.GetPlaylistsRequest.continuation_token', 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,
options=None),
_descriptor.FieldDescriptor(
name='sort_order', full_name='wireless_android_skyjam.GetPlaylistsRequest.sort_order', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=6829,
serialized_end=7026,
)
_GETPLAYLISTSRESPONSE = _descriptor.Descriptor(
name='GetPlaylistsResponse',
full_name='wireless_android_skyjam.GetPlaylistsResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.GetPlaylistsResponse.response_code', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist', full_name='wireless_android_skyjam.GetPlaylistsResponse.playlist', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='estimated_total_results', full_name='wireless_android_skyjam.GetPlaylistsResponse.estimated_total_results', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='continuation_token', full_name='wireless_android_skyjam.GetPlaylistsResponse.continuation_token', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_GETPLAYLISTSRESPONSE_RESPONSECODE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=7029,
serialized_end=7300,
)
_LOOKUPTRACKREQUEST = _descriptor.Descriptor(
name='LookupTrackRequest',
full_name='wireless_android_skyjam.LookupTrackRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.LookupTrackRequest.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,
options=None),
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.LookupTrackRequest.client_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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=7302,
serialized_end=7353,
)
_LOOKUPPLAYLISTENTRYREQUEST = _descriptor.Descriptor(
name='LookupPlaylistEntryRequest',
full_name='wireless_android_skyjam.LookupPlaylistEntryRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.LookupPlaylistEntryRequest.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,
options=None),
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.LookupPlaylistEntryRequest.client_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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=7355,
serialized_end=7414,
)
_LOOKUPPLAYLISTREQUEST = _descriptor.Descriptor(
name='LookupPlaylistRequest',
full_name='wireless_android_skyjam.LookupPlaylistRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.LookupPlaylistRequest.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,
options=None),
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.LookupPlaylistRequest.client_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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=7416,
serialized_end=7470,
)
_BATCHLOOKUPREQUEST = _descriptor.Descriptor(
name='BatchLookupRequest',
full_name='wireless_android_skyjam.BatchLookupRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.BatchLookupRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='track', full_name='wireless_android_skyjam.BatchLookupRequest.track', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist', full_name='wireless_android_skyjam.BatchLookupRequest.playlist', 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,
options=None),
_descriptor.FieldDescriptor(
name='metadata_type', full_name='wireless_android_skyjam.BatchLookupRequest.metadata_type', index=3,
number=4, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entry', full_name='wireless_android_skyjam.BatchLookupRequest.playlist_entry', 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,
options=None),
_descriptor.FieldDescriptor(
name='include_deleted', full_name='wireless_android_skyjam.BatchLookupRequest.include_deleted', index=5,
number=6, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_BATCHLOOKUPREQUEST_METADATATYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=7473,
serialized_end=7887,
)
_BATCHLOOKUPRESPONSE = _descriptor.Descriptor(
name='BatchLookupResponse',
full_name='wireless_android_skyjam.BatchLookupResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='track', full_name='wireless_android_skyjam.BatchLookupResponse.track', 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,
options=None),
_descriptor.FieldDescriptor(
name='playlist', full_name='wireless_android_skyjam.BatchLookupResponse.playlist', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entry', full_name='wireless_android_skyjam.BatchLookupResponse.playlist_entry', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=7890,
serialized_end=8075,
)
_MUTATETRACKREQUEST = _descriptor.Descriptor(
name='MutateTrackRequest',
full_name='wireless_android_skyjam.MutateTrackRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='create_track', full_name='wireless_android_skyjam.MutateTrackRequest.create_track', 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,
options=None),
_descriptor.FieldDescriptor(
name='update_track', full_name='wireless_android_skyjam.MutateTrackRequest.update_track', 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,
options=None),
_descriptor.FieldDescriptor(
name='delete_track', full_name='wireless_android_skyjam.MutateTrackRequest.delete_track', 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,
options=None),
_descriptor.FieldDescriptor(
name='partial_update', full_name='wireless_android_skyjam.MutateTrackRequest.partial_update', 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,
options=None),
_descriptor.FieldDescriptor(
name='update_last_modified', full_name='wireless_android_skyjam.MutateTrackRequest.update_last_modified', index=4,
number=5, 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,
options=None),
_descriptor.FieldDescriptor(
name='undelete_track', full_name='wireless_android_skyjam.MutateTrackRequest.undelete_track', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=8078,
serialized_end=8312,
)
_MUTATERESPONSE = _descriptor.Descriptor(
name='MutateResponse',
full_name='wireless_android_skyjam.MutateResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.MutateResponse.response_code', index=0,
number=1, 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,
options=None),
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.MutateResponse.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,
options=None),
_descriptor.FieldDescriptor(
name='child_id', full_name='wireless_android_skyjam.MutateResponse.child_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.MutateResponse.client_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='availability_status', full_name='wireless_android_skyjam.MutateResponse.availability_status', index=4,
number=5, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='error_message', full_name='wireless_android_skyjam.MutateResponse.error_message', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_MUTATERESPONSE_MUTATERESPONSECODE,
_MUTATERESPONSE_AVAILABILITYSTATUS,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=8315,
serialized_end=8801,
)
_BATCHMUTATETRACKSREQUEST = _descriptor.Descriptor(
name='BatchMutateTracksRequest',
full_name='wireless_android_skyjam.BatchMutateTracksRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.BatchMutateTracksRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='track_mutation', full_name='wireless_android_skyjam.BatchMutateTracksRequest.track_mutation', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='send_notification', full_name='wireless_android_skyjam.BatchMutateTracksRequest.send_notification', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='detect_timestamp_conflict', full_name='wireless_android_skyjam.BatchMutateTracksRequest.detect_timestamp_conflict', index=3,
number=4, 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,
options=None),
_descriptor.FieldDescriptor(
name='notify_fine_grained_updates', full_name='wireless_android_skyjam.BatchMutateTracksRequest.notify_fine_grained_updates', index=4,
number=5, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=8804,
serialized_end=9033,
)
_BATCHMUTATETRACKSRESPONSE = _descriptor.Descriptor(
name='BatchMutateTracksResponse',
full_name='wireless_android_skyjam.BatchMutateTracksResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.BatchMutateTracksResponse.response_code', index=0,
number=1, type=14, cpp_type=8, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='mutate_response', full_name='wireless_android_skyjam.BatchMutateTracksResponse.mutate_response', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_BATCHMUTATETRACKSRESPONSE_BATCHMUTATETRACKSRESPONSECODE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=9036,
serialized_end=9289,
)
_MUTATEPLAYLISTREQUEST = _descriptor.Descriptor(
name='MutatePlaylistRequest',
full_name='wireless_android_skyjam.MutatePlaylistRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='create_playlist', full_name='wireless_android_skyjam.MutatePlaylistRequest.create_playlist', 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,
options=None),
_descriptor.FieldDescriptor(
name='update_playlist', full_name='wireless_android_skyjam.MutatePlaylistRequest.update_playlist', 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,
options=None),
_descriptor.FieldDescriptor(
name='delete_playlist', full_name='wireless_android_skyjam.MutatePlaylistRequest.delete_playlist', 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,
options=None),
_descriptor.FieldDescriptor(
name='partial_update', full_name='wireless_android_skyjam.MutatePlaylistRequest.partial_update', 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,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entry', full_name='wireless_android_skyjam.MutatePlaylistRequest.playlist_entry', 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,
options=None),
_descriptor.FieldDescriptor(
name='update_last_modified', full_name='wireless_android_skyjam.MutatePlaylistRequest.update_last_modified', index=5,
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,
options=None),
_descriptor.FieldDescriptor(
name='undelete_playlist', full_name='wireless_android_skyjam.MutatePlaylistRequest.undelete_playlist', index=6,
number=7, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=9292,
serialized_end=9611,
)
_BATCHMUTATEPLAYLISTSREQUEST = _descriptor.Descriptor(
name='BatchMutatePlaylistsRequest',
full_name='wireless_android_skyjam.BatchMutatePlaylistsRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.BatchMutatePlaylistsRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist_mutation', full_name='wireless_android_skyjam.BatchMutatePlaylistsRequest.playlist_mutation', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='send_notification', full_name='wireless_android_skyjam.BatchMutatePlaylistsRequest.send_notification', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='detect_timestamp_conflict', full_name='wireless_android_skyjam.BatchMutatePlaylistsRequest.detect_timestamp_conflict', index=3,
number=4, 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,
options=None),
_descriptor.FieldDescriptor(
name='notify_fine_grained_updates', full_name='wireless_android_skyjam.BatchMutatePlaylistsRequest.notify_fine_grained_updates', index=4,
number=5, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=9614,
serialized_end=9853,
)
_BATCHMUTATEPLAYLISTSRESPONSE = _descriptor.Descriptor(
name='BatchMutatePlaylistsResponse',
full_name='wireless_android_skyjam.BatchMutatePlaylistsResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.BatchMutatePlaylistsResponse.response_code', index=0,
number=1, type=14, cpp_type=8, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='mutate_response', full_name='wireless_android_skyjam.BatchMutatePlaylistsResponse.mutate_response', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_BATCHMUTATEPLAYLISTSRESPONSE_BATCHMUTATEPLAYLISTSRESPONSECODE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=9856,
serialized_end=10121,
)
_MUTATEPLAYLISTENTRYREQUEST = _descriptor.Descriptor(
name='MutatePlaylistEntryRequest',
full_name='wireless_android_skyjam.MutatePlaylistEntryRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='create_playlist_entry', full_name='wireless_android_skyjam.MutatePlaylistEntryRequest.create_playlist_entry', 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,
options=None),
_descriptor.FieldDescriptor(
name='update_playlist_entry', full_name='wireless_android_skyjam.MutatePlaylistEntryRequest.update_playlist_entry', 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,
options=None),
_descriptor.FieldDescriptor(
name='delete_playlist_entry', full_name='wireless_android_skyjam.MutatePlaylistEntryRequest.delete_playlist_entry', 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,
options=None),
_descriptor.FieldDescriptor(
name='update_last_modified', full_name='wireless_android_skyjam.MutatePlaylistEntryRequest.update_last_modified', index=3,
number=4, 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,
options=None),
_descriptor.FieldDescriptor(
name='undelete_playlist_entry', full_name='wireless_android_skyjam.MutatePlaylistEntryRequest.undelete_playlist_entry', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=10124,
serialized_end=10434,
)
_BATCHMUTATEPLAYLISTENTRIESREQUEST = _descriptor.Descriptor(
name='BatchMutatePlaylistEntriesRequest',
full_name='wireless_android_skyjam.BatchMutatePlaylistEntriesRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.BatchMutatePlaylistEntriesRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entry_mutation', full_name='wireless_android_skyjam.BatchMutatePlaylistEntriesRequest.playlist_entry_mutation', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='send_notification', full_name='wireless_android_skyjam.BatchMutatePlaylistEntriesRequest.send_notification', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='detect_timestamp_conflict', full_name='wireless_android_skyjam.BatchMutatePlaylistEntriesRequest.detect_timestamp_conflict', index=3,
number=4, 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,
options=None),
_descriptor.FieldDescriptor(
name='notify_fine_grained_updates', full_name='wireless_android_skyjam.BatchMutatePlaylistEntriesRequest.notify_fine_grained_updates', index=4,
number=5, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=10437,
serialized_end=10693,
)
_BATCHMUTATEPLAYLISTENTRIESRESPONSE = _descriptor.Descriptor(
name='BatchMutatePlaylistEntriesResponse',
full_name='wireless_android_skyjam.BatchMutatePlaylistEntriesResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.BatchMutatePlaylistEntriesResponse.response_code', index=0,
number=1, type=14, cpp_type=8, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='mutate_response', full_name='wireless_android_skyjam.BatchMutatePlaylistEntriesResponse.mutate_response', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_BATCHMUTATEPLAYLISTENTRIESRESPONSE_BATCHMUTATEPLAYLISTENTRIESRESPONSECODE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=10696,
serialized_end=10985,
)
_MAGICPLAYLISTSEED = _descriptor.Descriptor(
name='MagicPlaylistSeed',
full_name='wireless_android_skyjam.MagicPlaylistSeed',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='seed_type', full_name='wireless_android_skyjam.MagicPlaylistSeed.seed_type', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='seed', full_name='wireless_android_skyjam.MagicPlaylistSeed.seed', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_MAGICPLAYLISTSEED_SEEDTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=10988,
serialized_end=11156,
)
_MAGICPLAYLISTREQUEST = _descriptor.Descriptor(
name='MagicPlaylistRequest',
full_name='wireless_android_skyjam.MagicPlaylistRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.MagicPlaylistRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist_name', full_name='wireless_android_skyjam.MagicPlaylistRequest.playlist_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,
options=None),
_descriptor.FieldDescriptor(
name='playlist_id', full_name='wireless_android_skyjam.MagicPlaylistRequest.playlist_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,
options=None),
_descriptor.FieldDescriptor(
name='seed', full_name='wireless_android_skyjam.MagicPlaylistRequest.seed', 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,
options=None),
_descriptor.FieldDescriptor(
name='num_recommendations', full_name='wireless_android_skyjam.MagicPlaylistRequest.num_recommendations', index=4,
number=5, 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,
options=None),
_descriptor.FieldDescriptor(
name='include_all_track_metadata', full_name='wireless_android_skyjam.MagicPlaylistRequest.include_all_track_metadata', index=5,
number=6, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='model_name', full_name='wireless_android_skyjam.MagicPlaylistRequest.model_name', index=6,
number=7, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=11159,
serialized_end=11392,
)
_MAGICPLAYLISTRESPONSE = _descriptor.Descriptor(
name='MagicPlaylistResponse',
full_name='wireless_android_skyjam.MagicPlaylistResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='playlist', full_name='wireless_android_skyjam.MagicPlaylistResponse.playlist', 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,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entry', full_name='wireless_android_skyjam.MagicPlaylistResponse.playlist_entry', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=11395,
serialized_end=11535,
)
_FLUSHLOCKERREQUEST = _descriptor.Descriptor(
name='FlushLockerRequest',
full_name='wireless_android_skyjam.FlushLockerRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.FlushLockerRequest.gaia_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='gaia_cookie', full_name='wireless_android_skyjam.FlushLockerRequest.gaia_cookie', 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,
options=None),
_descriptor.FieldDescriptor(
name='remove_audio_binaries', full_name='wireless_android_skyjam.FlushLockerRequest.remove_audio_binaries', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='remove_image_binaries', full_name='wireless_android_skyjam.FlushLockerRequest.remove_image_binaries', index=3,
number=4, 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,
options=None),
_descriptor.FieldDescriptor(
name='send_notification', full_name='wireless_android_skyjam.FlushLockerRequest.send_notification', index=4,
number=5, 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,
options=None),
_descriptor.FieldDescriptor(
name='reset_subscription_type', full_name='wireless_android_skyjam.FlushLockerRequest.reset_subscription_type', index=5,
number=6, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='notify_fine_grained_updates', full_name='wireless_android_skyjam.FlushLockerRequest.notify_fine_grained_updates', index=6,
number=8, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=11538,
serialized_end=11786,
)
_FLUSHLOCKERRESPONSE = _descriptor.Descriptor(
name='FlushLockerResponse',
full_name='wireless_android_skyjam.FlushLockerResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='tracks_removed', full_name='wireless_android_skyjam.FlushLockerResponse.tracks_removed', index=0,
number=1, type=5, cpp_type=1, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='entries_removed', full_name='wireless_android_skyjam.FlushLockerResponse.entries_removed', 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,
options=None),
_descriptor.FieldDescriptor(
name='playlists_removed', full_name='wireless_android_skyjam.FlushLockerResponse.playlists_removed', 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,
options=None),
_descriptor.FieldDescriptor(
name='success_reset_subscription_type', full_name='wireless_android_skyjam.FlushLockerResponse.success_reset_subscription_type', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=11789,
serialized_end=11927,
)
_LOCKERNOTIFICATION = _descriptor.Descriptor(
name='LockerNotification',
full_name='wireless_android_skyjam.LockerNotification',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.LockerNotification.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='payload', full_name='wireless_android_skyjam.LockerNotification.payload', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=11929,
serialized_end=11983,
)
_ALBUM = _descriptor.Descriptor(
name='Album',
full_name='wireless_android_skyjam.Album',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='name', full_name='wireless_android_skyjam.Album.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,
options=None),
_descriptor.FieldDescriptor(
name='album_artist', full_name='wireless_android_skyjam.Album.album_artist', 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,
options=None),
_descriptor.FieldDescriptor(
name='album_art', full_name='wireless_android_skyjam.Album.album_art', 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,
options=None),
_descriptor.FieldDescriptor(
name='track_count', full_name='wireless_android_skyjam.Album.track_count', 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,
options=None),
_descriptor.FieldDescriptor(
name='last_time_played', full_name='wireless_android_skyjam.Album.last_time_played', 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,
options=None),
_descriptor.FieldDescriptor(
name='is_compilation', full_name='wireless_android_skyjam.Album.is_compilation', 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,
options=None),
_descriptor.FieldDescriptor(
name='album_metajam_id', full_name='wireless_android_skyjam.Album.album_metajam_id', index=6,
number=7, type=9, cpp_type=9, label=1,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='creation_timestamp', full_name='wireless_android_skyjam.Album.creation_timestamp', index=7,
number=8, 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,
options=None),
_descriptor.FieldDescriptor(
name='artist', full_name='wireless_android_skyjam.Album.artist', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=11986,
serialized_end=12224,
)
_ALBUMSORTORDER = _descriptor.Descriptor(
name='AlbumSortOrder',
full_name='wireless_android_skyjam.AlbumSortOrder',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='attribute', full_name='wireless_android_skyjam.AlbumSortOrder.attribute', index=0,
number=1, 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,
options=None),
_descriptor.FieldDescriptor(
name='descending', full_name='wireless_android_skyjam.AlbumSortOrder.descending', index=1,
number=2, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_ALBUMSORTORDER_ALBUMATTRIBUTE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=12227,
serialized_end=12414,
)
_GETALBUMSREQUEST = _descriptor.Descriptor(
name='GetAlbumsRequest',
full_name='wireless_android_skyjam.GetAlbumsRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.GetAlbumsRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='sort_order', full_name='wireless_android_skyjam.GetAlbumsRequest.sort_order', 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,
options=None),
_descriptor.FieldDescriptor(
name='max_results', full_name='wireless_android_skyjam.GetAlbumsRequest.max_results', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=12416,
serialized_end=12533,
)
_GETALBUMSRESPONSE = _descriptor.Descriptor(
name='GetAlbumsResponse',
full_name='wireless_android_skyjam.GetAlbumsResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='album', full_name='wireless_android_skyjam.GetAlbumsResponse.album', index=0,
number=1, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=12535,
serialized_end=12601,
)
_ARTIST = _descriptor.Descriptor(
name='Artist',
full_name='wireless_android_skyjam.Artist',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='name', full_name='wireless_android_skyjam.Artist.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,
options=None),
_descriptor.FieldDescriptor(
name='total_track_count', full_name='wireless_android_skyjam.Artist.total_track_count', 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,
options=None),
_descriptor.FieldDescriptor(
name='album', full_name='wireless_android_skyjam.Artist.album', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=12603,
serialized_end=12699,
)
_ARTISTSORTORDER = _descriptor.Descriptor(
name='ArtistSortOrder',
full_name='wireless_android_skyjam.ArtistSortOrder',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='descending', full_name='wireless_android_skyjam.ArtistSortOrder.descending', index=0,
number=2, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=12701,
serialized_end=12745,
)
_GETARTISTSREQUEST = _descriptor.Descriptor(
name='GetArtistsRequest',
full_name='wireless_android_skyjam.GetArtistsRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.GetArtistsRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='sort_order', full_name='wireless_android_skyjam.GetArtistsRequest.sort_order', 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,
options=None),
_descriptor.FieldDescriptor(
name='max_results', full_name='wireless_android_skyjam.GetArtistsRequest.max_results', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=12747,
serialized_end=12866,
)
_GETARTISTSRESPONSE = _descriptor.Descriptor(
name='GetArtistsResponse',
full_name='wireless_android_skyjam.GetArtistsResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='artist', full_name='wireless_android_skyjam.GetArtistsResponse.artist', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=12868,
serialized_end=12937,
)
_MUSICGENRE = _descriptor.Descriptor(
name='MusicGenre',
full_name='wireless_android_skyjam.MusicGenre',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='name', full_name='wireless_android_skyjam.MusicGenre.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,
options=None),
_descriptor.FieldDescriptor(
name='total_track_count', full_name='wireless_android_skyjam.MusicGenre.total_track_count', 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,
options=None),
_descriptor.FieldDescriptor(
name='album', full_name='wireless_android_skyjam.MusicGenre.album', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=12939,
serialized_end=13039,
)
_GENRESORTORDER = _descriptor.Descriptor(
name='GenreSortOrder',
full_name='wireless_android_skyjam.GenreSortOrder',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='descending', full_name='wireless_android_skyjam.GenreSortOrder.descending', index=0,
number=2, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=13041,
serialized_end=13084,
)
_GETGENRESREQUEST = _descriptor.Descriptor(
name='GetGenresRequest',
full_name='wireless_android_skyjam.GetGenresRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.GetGenresRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='sort_order', full_name='wireless_android_skyjam.GetGenresRequest.sort_order', 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,
options=None),
_descriptor.FieldDescriptor(
name='max_results', full_name='wireless_android_skyjam.GetGenresRequest.max_results', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=13086,
serialized_end=13203,
)
_GETGENRESRESPONSE = _descriptor.Descriptor(
name='GetGenresResponse',
full_name='wireless_android_skyjam.GetGenresResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='genre', full_name='wireless_android_skyjam.GetGenresResponse.genre', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=13205,
serialized_end=13276,
)
_GETDYNAMICPLAYLISTENTRIESREQUEST = _descriptor.Descriptor(
name='GetDynamicPlaylistEntriesRequest',
full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entries_type', full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesRequest.playlist_entries_type', index=1,
number=4, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='max_results', full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesRequest.max_results', index=2,
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,
options=None),
_descriptor.FieldDescriptor(
name='continuation_token', full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesRequest.continuation_token', index=3,
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,
options=None),
_descriptor.FieldDescriptor(
name='include_all_track_metadata', full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesRequest.include_all_track_metadata', index=4,
number=5, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_GETDYNAMICPLAYLISTENTRIESREQUEST_DYNAMICPLAYLISTENTRIESTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=13279,
serialized_end=13661,
)
_GETDYNAMICPLAYLISTENTRIESRESPONSE = _descriptor.Descriptor(
name='GetDynamicPlaylistEntriesResponse',
full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesResponse.response_code', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entry', full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesResponse.playlist_entry', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='estimated_total_results', full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesResponse.estimated_total_results', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='continuation_token', full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesResponse.continuation_token', 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,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entries_type', full_name='wireless_android_skyjam.GetDynamicPlaylistEntriesResponse.playlist_entries_type', index=4,
number=5, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_GETDYNAMICPLAYLISTENTRIESRESPONSE_DYNAMICPLAYLISTENTRIESTYPE,
_GETDYNAMICPLAYLISTENTRIESRESPONSE_RESPONSECODE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=13664,
serialized_end=14210,
)
_GETAGGREGATIONSBYTRACKTYPEREQUEST = _descriptor.Descriptor(
name='GetAggregationsByTrackTypeRequest',
full_name='wireless_android_skyjam.GetAggregationsByTrackTypeRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.GetAggregationsByTrackTypeRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=14212,
serialized_end=14264,
)
_TRACKTYPEAGGREGATE = _descriptor.Descriptor(
name='TrackTypeAggregate',
full_name='wireless_android_skyjam.TrackTypeAggregate',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='track_type_value', full_name='wireless_android_skyjam.TrackTypeAggregate.track_type_value', index=0,
number=1, 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,
options=None),
_descriptor.FieldDescriptor(
name='count', full_name='wireless_android_skyjam.TrackTypeAggregate.count', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_TRACKTYPEAGGREGATE_TRACKTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=14267,
serialized_end=14525,
)
_GETAGGREGATIONSBYTRACKTYPERESPONSE = _descriptor.Descriptor(
name='GetAggregationsByTrackTypeResponse',
full_name='wireless_android_skyjam.GetAggregationsByTrackTypeResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='track_type_aggregate', full_name='wireless_android_skyjam.GetAggregationsByTrackTypeResponse.track_type_aggregate', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=14527,
serialized_end=14638,
)
_GETAGGREGATIONSBYAVAILABILITYSTATUSREQUEST = _descriptor.Descriptor(
name='GetAggregationsByAvailabilityStatusRequest',
full_name='wireless_android_skyjam.GetAggregationsByAvailabilityStatusRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.GetAggregationsByAvailabilityStatusRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=14640,
serialized_end=14701,
)
_AVAILABILITYSTATUSAGGREGATE = _descriptor.Descriptor(
name='AvailabilityStatusAggregate',
full_name='wireless_android_skyjam.AvailabilityStatusAggregate',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='availability_status', full_name='wireless_android_skyjam.AvailabilityStatusAggregate.availability_status', index=0,
number=1, 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,
options=None),
_descriptor.FieldDescriptor(
name='count', full_name='wireless_android_skyjam.AvailabilityStatusAggregate.count', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_AVAILABILITYSTATUSAGGREGATE_AVAILABILITYSTATUS,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=14704,
serialized_end=14987,
)
_GETAGGREGATIONSBYAVAILABILITYSTATUSRESPONSE = _descriptor.Descriptor(
name='GetAggregationsByAvailabilityStatusResponse',
full_name='wireless_android_skyjam.GetAggregationsByAvailabilityStatusResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='availability_status_aggregate', full_name='wireless_android_skyjam.GetAggregationsByAvailabilityStatusResponse.availability_status_aggregate', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=14990,
serialized_end=15128,
)
_ADDPROMOTRACKSREQUEST = _descriptor.Descriptor(
name='AddPromoTracksRequest',
full_name='wireless_android_skyjam.AddPromoTracksRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.AddPromoTracksRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='genre', full_name='wireless_android_skyjam.AddPromoTracksRequest.genre', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=15130,
serialized_end=15185,
)
_ADDPROMOTRACKSRESPONSE = _descriptor.Descriptor(
name='AddPromoTracksResponse',
full_name='wireless_android_skyjam.AddPromoTracksResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='track', full_name='wireless_android_skyjam.AddPromoTracksResponse.track', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=15187,
serialized_end=15258,
)
_GETPLAYLISTAGGREGATIONSREQUEST = _descriptor.Descriptor(
name='GetPlaylistAggregationsRequest',
full_name='wireless_android_skyjam.GetPlaylistAggregationsRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.GetPlaylistAggregationsRequest.gaia_id', index=0,
number=1, type=3, cpp_type=2, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='max_results', full_name='wireless_android_skyjam.GetPlaylistAggregationsRequest.max_results', index=1,
number=2, type=5, cpp_type=1, label=1,
has_default_value=True, default_value=14,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=15260,
serialized_end=15334,
)
_PLAYLISTAGGREGATE = _descriptor.Descriptor(
name='PlaylistAggregate',
full_name='wireless_android_skyjam.PlaylistAggregate',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='playlist_id', full_name='wireless_android_skyjam.PlaylistAggregate.playlist_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,
options=None),
_descriptor.FieldDescriptor(
name='name', full_name='wireless_android_skyjam.PlaylistAggregate.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,
options=None),
_descriptor.FieldDescriptor(
name='album_art', full_name='wireless_android_skyjam.PlaylistAggregate.album_art', 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,
options=None),
_descriptor.FieldDescriptor(
name='track_count', full_name='wireless_android_skyjam.PlaylistAggregate.track_count', index=3,
number=4, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='last_time_played', full_name='wireless_android_skyjam.PlaylistAggregate.last_time_played', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=15337,
serialized_end=15492,
)
_GETPLAYLISTAGGREGATIONSRESPONSE = _descriptor.Descriptor(
name='GetPlaylistAggregationsResponse',
full_name='wireless_android_skyjam.GetPlaylistAggregationsResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='playlist_aggregate', full_name='wireless_android_skyjam.GetPlaylistAggregationsResponse.playlist_aggregate', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=15494,
serialized_end=15599,
)
_REMOTECONTROLCOMMANDREQUEST = _descriptor.Descriptor(
name='RemoteControlCommandRequest',
full_name='wireless_android_skyjam.RemoteControlCommandRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaia_id', full_name='wireless_android_skyjam.RemoteControlCommandRequest.gaia_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='command', full_name='wireless_android_skyjam.RemoteControlCommandRequest.command', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=15601,
serialized_end=15664,
)
_REMOTECONTROLCOMMANDRESPONSE = _descriptor.Descriptor(
name='RemoteControlCommandResponse',
full_name='wireless_android_skyjam.RemoteControlCommandResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.RemoteControlCommandResponse.response_code', index=0,
number=1, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_REMOTECONTROLCOMMANDRESPONSE_RESPONSECODE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=15667,
serialized_end=15870,
)
_AUDIOREF.fields_by_name['store'].enum_type = _AUDIOREF_STORE
_AUDIOREF_STORE.containing_type = _AUDIOREF
_IMAGEREF.fields_by_name['store'].enum_type = _IMAGEREF_STORE
_IMAGEREF.fields_by_name['origin'].enum_type = _IMAGEREF_ORIGIN
_IMAGEREF_STORE.containing_type = _IMAGEREF
_IMAGEREF_ORIGIN.containing_type = _IMAGEREF
_TRACKEXTRAS.fields_by_name['additional_metadata'].message_type = _ADDITIONALMETADATA
_TRACK.fields_by_name['audio_ref'].message_type = _AUDIOREF
_TRACK.fields_by_name['album_art_ref'].message_type = _IMAGEREF
_TRACK.fields_by_name['availability_status'].enum_type = _TRACK_AVAILABILITYSTATUS
_TRACK.fields_by_name['content_type'].enum_type = _TRACK_CONTENTTYPE
_TRACK.fields_by_name['channels'].enum_type = _TRACK_CHANNELS
_TRACK.fields_by_name['track_type'].enum_type = _TRACK_TRACKTYPE
_TRACK.fields_by_name['rating'].enum_type = _TRACK_RATING
_TRACK.fields_by_name['uits_metadata'].message_type = uits__pb2._UITSMETADATA
_TRACK.fields_by_name['original_content_type'].enum_type = _TRACK_CONTENTTYPE
_TRACK.fields_by_name['uploaded_uits'].message_type = _UPLOADEDUITSID3TAG
_TRACK.fields_by_name['explicit_type'].enum_type = _TRACK_EXPLICITTYPE
_TRACK.fields_by_name['track_extras'].message_type = _TRACKEXTRAS
_TRACK_AVAILABILITYSTATUS.containing_type = _TRACK
_TRACK_CONTENTTYPE.containing_type = _TRACK
_TRACK_CHANNELS.containing_type = _TRACK
_TRACK_TRACKTYPE.containing_type = _TRACK
_TRACK_RATING.containing_type = _TRACK
_TRACK_EXPLICITTYPE.containing_type = _TRACK
_TRACKS.fields_by_name['track'].message_type = _TRACK
_PLAYLIST.fields_by_name['playlist_type'].enum_type = _PLAYLIST_PLAYLISTTYPE
_PLAYLIST.fields_by_name['playlist_art_ref'].message_type = _IMAGEREF
_PLAYLIST_PLAYLISTTYPE.containing_type = _PLAYLIST
_PLAYLISTENTRY.fields_by_name['relative_position_id_type'].enum_type = _PLAYLISTENTRY_RELATIVEPOSITIONIDTYPE
_PLAYLISTENTRY.fields_by_name['track'].message_type = _TRACK
_PLAYLISTENTRY_RELATIVEPOSITIONIDTYPE.containing_type = _PLAYLISTENTRY
_TRACKSEARCHRESTRICTION.fields_by_name['attribute'].enum_type = _TRACKSEARCHRESTRICTION_TRACKATTRIBUTE
_TRACKSEARCHRESTRICTION.fields_by_name['comparison_type'].enum_type = _TRACKSEARCHRESTRICTION_COMPARISONTYPE
_TRACKSEARCHRESTRICTION_TRACKATTRIBUTE.containing_type = _TRACKSEARCHRESTRICTION
_TRACKSEARCHRESTRICTION_COMPARISONTYPE.containing_type = _TRACKSEARCHRESTRICTION
_TRACKSEARCHRESTRICTIONSET.fields_by_name['type'].enum_type = _TRACKSEARCHRESTRICTIONSET_RESTRICTIONSETTYPE
_TRACKSEARCHRESTRICTIONSET.fields_by_name['restriction'].message_type = _TRACKSEARCHRESTRICTION
_TRACKSEARCHRESTRICTIONSET.fields_by_name['sub_set'].message_type = _TRACKSEARCHRESTRICTIONSET
_TRACKSEARCHRESTRICTIONSET_RESTRICTIONSETTYPE.containing_type = _TRACKSEARCHRESTRICTIONSET
_TRACKSORTORDER.fields_by_name['attribute'].enum_type = _TRACKSORTORDER_TRACKATTRIBUTE
_TRACKSORTORDER_TRACKATTRIBUTE.containing_type = _TRACKSORTORDER
_GETTRACKSREQUEST.fields_by_name['search_restriction'].message_type = _TRACKSEARCHRESTRICTION
_GETTRACKSREQUEST.fields_by_name['sort_order'].message_type = _TRACKSORTORDER
_GETTRACKSREQUEST.fields_by_name['restriction_set'].message_type = _TRACKSEARCHRESTRICTIONSET
_GETTRACKSREQUEST.fields_by_name['track_projection'].enum_type = _GETTRACKSREQUEST_TRACKPROJECTION
_GETTRACKSREQUEST_TRACKPROJECTION.containing_type = _GETTRACKSREQUEST
_GETTRACKSRESPONSE.fields_by_name['response_code'].enum_type = _GETTRACKSRESPONSE_RESPONSECODE
_GETTRACKSRESPONSE.fields_by_name['track'].message_type = _TRACK
_GETTRACKSRESPONSE_RESPONSECODE.containing_type = _GETTRACKSRESPONSE
_GETPLAYLISTENTRIESRESPONSE.fields_by_name['response_code'].enum_type = _GETPLAYLISTENTRIESRESPONSE_RESPONSECODE
_GETPLAYLISTENTRIESRESPONSE.fields_by_name['playlist_entry'].message_type = _PLAYLISTENTRY
_GETPLAYLISTENTRIESRESPONSE_RESPONSECODE.containing_type = _GETPLAYLISTENTRIESRESPONSE
_PLAYLISTSORTORDER.fields_by_name['attribute'].enum_type = _PLAYLISTSORTORDER_PLAYLISTATTRIBUTE
_PLAYLISTSORTORDER_PLAYLISTATTRIBUTE.containing_type = _PLAYLISTSORTORDER
_GETPLAYLISTSREQUEST.fields_by_name['sort_order'].message_type = _PLAYLISTSORTORDER
_GETPLAYLISTSRESPONSE.fields_by_name['response_code'].enum_type = _GETPLAYLISTSRESPONSE_RESPONSECODE
_GETPLAYLISTSRESPONSE.fields_by_name['playlist'].message_type = _PLAYLIST
_GETPLAYLISTSRESPONSE_RESPONSECODE.containing_type = _GETPLAYLISTSRESPONSE
_BATCHLOOKUPREQUEST.fields_by_name['track'].message_type = _LOOKUPTRACKREQUEST
_BATCHLOOKUPREQUEST.fields_by_name['playlist'].message_type = _LOOKUPPLAYLISTREQUEST
_BATCHLOOKUPREQUEST.fields_by_name['metadata_type'].enum_type = _BATCHLOOKUPREQUEST_METADATATYPE
_BATCHLOOKUPREQUEST.fields_by_name['playlist_entry'].message_type = _LOOKUPPLAYLISTENTRYREQUEST
_BATCHLOOKUPREQUEST_METADATATYPE.containing_type = _BATCHLOOKUPREQUEST
_BATCHLOOKUPRESPONSE.fields_by_name['track'].message_type = _TRACK
_BATCHLOOKUPRESPONSE.fields_by_name['playlist'].message_type = _PLAYLIST
_BATCHLOOKUPRESPONSE.fields_by_name['playlist_entry'].message_type = _PLAYLISTENTRY
_MUTATETRACKREQUEST.fields_by_name['create_track'].message_type = _TRACK
_MUTATETRACKREQUEST.fields_by_name['update_track'].message_type = _TRACK
_MUTATERESPONSE.fields_by_name['response_code'].enum_type = _MUTATERESPONSE_MUTATERESPONSECODE
_MUTATERESPONSE.fields_by_name['availability_status'].enum_type = _MUTATERESPONSE_AVAILABILITYSTATUS
_MUTATERESPONSE_MUTATERESPONSECODE.containing_type = _MUTATERESPONSE
_MUTATERESPONSE_AVAILABILITYSTATUS.containing_type = _MUTATERESPONSE
_BATCHMUTATETRACKSREQUEST.fields_by_name['track_mutation'].message_type = _MUTATETRACKREQUEST
_BATCHMUTATETRACKSRESPONSE.fields_by_name['response_code'].enum_type = _BATCHMUTATETRACKSRESPONSE_BATCHMUTATETRACKSRESPONSECODE
_BATCHMUTATETRACKSRESPONSE.fields_by_name['mutate_response'].message_type = _MUTATERESPONSE
_BATCHMUTATETRACKSRESPONSE_BATCHMUTATETRACKSRESPONSECODE.containing_type = _BATCHMUTATETRACKSRESPONSE
_MUTATEPLAYLISTREQUEST.fields_by_name['create_playlist'].message_type = _PLAYLIST
_MUTATEPLAYLISTREQUEST.fields_by_name['update_playlist'].message_type = _PLAYLIST
_MUTATEPLAYLISTREQUEST.fields_by_name['playlist_entry'].message_type = _PLAYLISTENTRY
_BATCHMUTATEPLAYLISTSREQUEST.fields_by_name['playlist_mutation'].message_type = _MUTATEPLAYLISTREQUEST
_BATCHMUTATEPLAYLISTSRESPONSE.fields_by_name['response_code'].enum_type = _BATCHMUTATEPLAYLISTSRESPONSE_BATCHMUTATEPLAYLISTSRESPONSECODE
_BATCHMUTATEPLAYLISTSRESPONSE.fields_by_name['mutate_response'].message_type = _MUTATERESPONSE
_BATCHMUTATEPLAYLISTSRESPONSE_BATCHMUTATEPLAYLISTSRESPONSECODE.containing_type = _BATCHMUTATEPLAYLISTSRESPONSE
_MUTATEPLAYLISTENTRYREQUEST.fields_by_name['create_playlist_entry'].message_type = _PLAYLISTENTRY
_MUTATEPLAYLISTENTRYREQUEST.fields_by_name['update_playlist_entry'].message_type = _PLAYLISTENTRY
_MUTATEPLAYLISTENTRYREQUEST.fields_by_name['delete_playlist_entry'].message_type = _PLAYLISTENTRY
_BATCHMUTATEPLAYLISTENTRIESREQUEST.fields_by_name['playlist_entry_mutation'].message_type = _MUTATEPLAYLISTENTRYREQUEST
_BATCHMUTATEPLAYLISTENTRIESRESPONSE.fields_by_name['response_code'].enum_type = _BATCHMUTATEPLAYLISTENTRIESRESPONSE_BATCHMUTATEPLAYLISTENTRIESRESPONSECODE
_BATCHMUTATEPLAYLISTENTRIESRESPONSE.fields_by_name['mutate_response'].message_type = _MUTATERESPONSE
_BATCHMUTATEPLAYLISTENTRIESRESPONSE_BATCHMUTATEPLAYLISTENTRIESRESPONSECODE.containing_type = _BATCHMUTATEPLAYLISTENTRIESRESPONSE
_MAGICPLAYLISTSEED.fields_by_name['seed_type'].enum_type = _MAGICPLAYLISTSEED_SEEDTYPE
_MAGICPLAYLISTSEED_SEEDTYPE.containing_type = _MAGICPLAYLISTSEED
_MAGICPLAYLISTREQUEST.fields_by_name['seed'].message_type = _MAGICPLAYLISTSEED
_MAGICPLAYLISTRESPONSE.fields_by_name['playlist'].message_type = _PLAYLIST
_MAGICPLAYLISTRESPONSE.fields_by_name['playlist_entry'].message_type = _PLAYLISTENTRY
_ALBUM.fields_by_name['album_art'].message_type = _IMAGEREF
_ALBUMSORTORDER.fields_by_name['attribute'].enum_type = _ALBUMSORTORDER_ALBUMATTRIBUTE
_ALBUMSORTORDER_ALBUMATTRIBUTE.containing_type = _ALBUMSORTORDER
_GETALBUMSREQUEST.fields_by_name['sort_order'].message_type = _ALBUMSORTORDER
_GETALBUMSRESPONSE.fields_by_name['album'].message_type = _ALBUM
_ARTIST.fields_by_name['album'].message_type = _ALBUM
_GETARTISTSREQUEST.fields_by_name['sort_order'].message_type = _ARTISTSORTORDER
_GETARTISTSRESPONSE.fields_by_name['artist'].message_type = _ARTIST
_MUSICGENRE.fields_by_name['album'].message_type = _ALBUM
_GETGENRESREQUEST.fields_by_name['sort_order'].message_type = _GENRESORTORDER
_GETGENRESRESPONSE.fields_by_name['genre'].message_type = _MUSICGENRE
_GETDYNAMICPLAYLISTENTRIESREQUEST.fields_by_name['playlist_entries_type'].enum_type = _GETDYNAMICPLAYLISTENTRIESREQUEST_DYNAMICPLAYLISTENTRIESTYPE
_GETDYNAMICPLAYLISTENTRIESREQUEST_DYNAMICPLAYLISTENTRIESTYPE.containing_type = _GETDYNAMICPLAYLISTENTRIESREQUEST
_GETDYNAMICPLAYLISTENTRIESRESPONSE.fields_by_name['response_code'].enum_type = _GETDYNAMICPLAYLISTENTRIESRESPONSE_RESPONSECODE
_GETDYNAMICPLAYLISTENTRIESRESPONSE.fields_by_name['playlist_entry'].message_type = _PLAYLISTENTRY
_GETDYNAMICPLAYLISTENTRIESRESPONSE.fields_by_name['playlist_entries_type'].enum_type = _GETDYNAMICPLAYLISTENTRIESRESPONSE_DYNAMICPLAYLISTENTRIESTYPE
_GETDYNAMICPLAYLISTENTRIESRESPONSE_DYNAMICPLAYLISTENTRIESTYPE.containing_type = _GETDYNAMICPLAYLISTENTRIESRESPONSE
_GETDYNAMICPLAYLISTENTRIESRESPONSE_RESPONSECODE.containing_type = _GETDYNAMICPLAYLISTENTRIESRESPONSE
_TRACKTYPEAGGREGATE.fields_by_name['track_type_value'].enum_type = _TRACKTYPEAGGREGATE_TRACKTYPE
_TRACKTYPEAGGREGATE_TRACKTYPE.containing_type = _TRACKTYPEAGGREGATE
_GETAGGREGATIONSBYTRACKTYPERESPONSE.fields_by_name['track_type_aggregate'].message_type = _TRACKTYPEAGGREGATE
_AVAILABILITYSTATUSAGGREGATE.fields_by_name['availability_status'].enum_type = _AVAILABILITYSTATUSAGGREGATE_AVAILABILITYSTATUS
_AVAILABILITYSTATUSAGGREGATE_AVAILABILITYSTATUS.containing_type = _AVAILABILITYSTATUSAGGREGATE
_GETAGGREGATIONSBYAVAILABILITYSTATUSRESPONSE.fields_by_name['availability_status_aggregate'].message_type = _AVAILABILITYSTATUSAGGREGATE
_ADDPROMOTRACKSRESPONSE.fields_by_name['track'].message_type = _TRACK
_PLAYLISTAGGREGATE.fields_by_name['album_art'].message_type = _IMAGEREF
_GETPLAYLISTAGGREGATIONSRESPONSE.fields_by_name['playlist_aggregate'].message_type = _PLAYLISTAGGREGATE
_REMOTECONTROLCOMMANDRESPONSE.fields_by_name['response_code'].enum_type = _REMOTECONTROLCOMMANDRESPONSE_RESPONSECODE
_REMOTECONTROLCOMMANDRESPONSE_RESPONSECODE.containing_type = _REMOTECONTROLCOMMANDRESPONSE
DESCRIPTOR.message_types_by_name['AudioRef'] = _AUDIOREF
DESCRIPTOR.message_types_by_name['ImageRef'] = _IMAGEREF
DESCRIPTOR.message_types_by_name['UploadedUitsId3Tag'] = _UPLOADEDUITSID3TAG
DESCRIPTOR.message_types_by_name['AdditionalMetadata'] = _ADDITIONALMETADATA
DESCRIPTOR.message_types_by_name['TrackExtras'] = _TRACKEXTRAS
DESCRIPTOR.message_types_by_name['Track'] = _TRACK
DESCRIPTOR.message_types_by_name['Tracks'] = _TRACKS
DESCRIPTOR.message_types_by_name['Playlist'] = _PLAYLIST
DESCRIPTOR.message_types_by_name['PlaylistEntry'] = _PLAYLISTENTRY
DESCRIPTOR.message_types_by_name['TrackSearchRestriction'] = _TRACKSEARCHRESTRICTION
DESCRIPTOR.message_types_by_name['TrackSearchRestrictionSet'] = _TRACKSEARCHRESTRICTIONSET
DESCRIPTOR.message_types_by_name['TrackSortOrder'] = _TRACKSORTORDER
DESCRIPTOR.message_types_by_name['GetTracksRequest'] = _GETTRACKSREQUEST
DESCRIPTOR.message_types_by_name['GetTracksResponse'] = _GETTRACKSRESPONSE
DESCRIPTOR.message_types_by_name['GetPlaylistEntriesRequest'] = _GETPLAYLISTENTRIESREQUEST
DESCRIPTOR.message_types_by_name['GetPlaylistEntriesResponse'] = _GETPLAYLISTENTRIESRESPONSE
DESCRIPTOR.message_types_by_name['PlaylistSortOrder'] = _PLAYLISTSORTORDER
DESCRIPTOR.message_types_by_name['GetPlaylistsRequest'] = _GETPLAYLISTSREQUEST
DESCRIPTOR.message_types_by_name['GetPlaylistsResponse'] = _GETPLAYLISTSRESPONSE
DESCRIPTOR.message_types_by_name['LookupTrackRequest'] = _LOOKUPTRACKREQUEST
DESCRIPTOR.message_types_by_name['LookupPlaylistEntryRequest'] = _LOOKUPPLAYLISTENTRYREQUEST
DESCRIPTOR.message_types_by_name['LookupPlaylistRequest'] = _LOOKUPPLAYLISTREQUEST
DESCRIPTOR.message_types_by_name['BatchLookupRequest'] = _BATCHLOOKUPREQUEST
DESCRIPTOR.message_types_by_name['BatchLookupResponse'] = _BATCHLOOKUPRESPONSE
DESCRIPTOR.message_types_by_name['MutateTrackRequest'] = _MUTATETRACKREQUEST
DESCRIPTOR.message_types_by_name['MutateResponse'] = _MUTATERESPONSE
DESCRIPTOR.message_types_by_name['BatchMutateTracksRequest'] = _BATCHMUTATETRACKSREQUEST
DESCRIPTOR.message_types_by_name['BatchMutateTracksResponse'] = _BATCHMUTATETRACKSRESPONSE
DESCRIPTOR.message_types_by_name['MutatePlaylistRequest'] = _MUTATEPLAYLISTREQUEST
DESCRIPTOR.message_types_by_name['BatchMutatePlaylistsRequest'] = _BATCHMUTATEPLAYLISTSREQUEST
DESCRIPTOR.message_types_by_name['BatchMutatePlaylistsResponse'] = _BATCHMUTATEPLAYLISTSRESPONSE
DESCRIPTOR.message_types_by_name['MutatePlaylistEntryRequest'] = _MUTATEPLAYLISTENTRYREQUEST
DESCRIPTOR.message_types_by_name['BatchMutatePlaylistEntriesRequest'] = _BATCHMUTATEPLAYLISTENTRIESREQUEST
DESCRIPTOR.message_types_by_name['BatchMutatePlaylistEntriesResponse'] = _BATCHMUTATEPLAYLISTENTRIESRESPONSE
DESCRIPTOR.message_types_by_name['MagicPlaylistSeed'] = _MAGICPLAYLISTSEED
DESCRIPTOR.message_types_by_name['MagicPlaylistRequest'] = _MAGICPLAYLISTREQUEST
DESCRIPTOR.message_types_by_name['MagicPlaylistResponse'] = _MAGICPLAYLISTRESPONSE
DESCRIPTOR.message_types_by_name['FlushLockerRequest'] = _FLUSHLOCKERREQUEST
DESCRIPTOR.message_types_by_name['FlushLockerResponse'] = _FLUSHLOCKERRESPONSE
DESCRIPTOR.message_types_by_name['LockerNotification'] = _LOCKERNOTIFICATION
DESCRIPTOR.message_types_by_name['Album'] = _ALBUM
DESCRIPTOR.message_types_by_name['AlbumSortOrder'] = _ALBUMSORTORDER
DESCRIPTOR.message_types_by_name['GetAlbumsRequest'] = _GETALBUMSREQUEST
DESCRIPTOR.message_types_by_name['GetAlbumsResponse'] = _GETALBUMSRESPONSE
DESCRIPTOR.message_types_by_name['Artist'] = _ARTIST
DESCRIPTOR.message_types_by_name['ArtistSortOrder'] = _ARTISTSORTORDER
DESCRIPTOR.message_types_by_name['GetArtistsRequest'] = _GETARTISTSREQUEST
DESCRIPTOR.message_types_by_name['GetArtistsResponse'] = _GETARTISTSRESPONSE
DESCRIPTOR.message_types_by_name['MusicGenre'] = _MUSICGENRE
DESCRIPTOR.message_types_by_name['GenreSortOrder'] = _GENRESORTORDER
DESCRIPTOR.message_types_by_name['GetGenresRequest'] = _GETGENRESREQUEST
DESCRIPTOR.message_types_by_name['GetGenresResponse'] = _GETGENRESRESPONSE
DESCRIPTOR.message_types_by_name['GetDynamicPlaylistEntriesRequest'] = _GETDYNAMICPLAYLISTENTRIESREQUEST
DESCRIPTOR.message_types_by_name['GetDynamicPlaylistEntriesResponse'] = _GETDYNAMICPLAYLISTENTRIESRESPONSE
DESCRIPTOR.message_types_by_name['GetAggregationsByTrackTypeRequest'] = _GETAGGREGATIONSBYTRACKTYPEREQUEST
DESCRIPTOR.message_types_by_name['TrackTypeAggregate'] = _TRACKTYPEAGGREGATE
DESCRIPTOR.message_types_by_name['GetAggregationsByTrackTypeResponse'] = _GETAGGREGATIONSBYTRACKTYPERESPONSE
DESCRIPTOR.message_types_by_name['GetAggregationsByAvailabilityStatusRequest'] = _GETAGGREGATIONSBYAVAILABILITYSTATUSREQUEST
DESCRIPTOR.message_types_by_name['AvailabilityStatusAggregate'] = _AVAILABILITYSTATUSAGGREGATE
DESCRIPTOR.message_types_by_name['GetAggregationsByAvailabilityStatusResponse'] = _GETAGGREGATIONSBYAVAILABILITYSTATUSRESPONSE
DESCRIPTOR.message_types_by_name['AddPromoTracksRequest'] = _ADDPROMOTRACKSREQUEST
DESCRIPTOR.message_types_by_name['AddPromoTracksResponse'] = _ADDPROMOTRACKSRESPONSE
DESCRIPTOR.message_types_by_name['GetPlaylistAggregationsRequest'] = _GETPLAYLISTAGGREGATIONSREQUEST
DESCRIPTOR.message_types_by_name['PlaylistAggregate'] = _PLAYLISTAGGREGATE
DESCRIPTOR.message_types_by_name['GetPlaylistAggregationsResponse'] = _GETPLAYLISTAGGREGATIONSRESPONSE
DESCRIPTOR.message_types_by_name['RemoteControlCommandRequest'] = _REMOTECONTROLCOMMANDREQUEST
DESCRIPTOR.message_types_by_name['RemoteControlCommandResponse'] = _REMOTECONTROLCOMMANDRESPONSE
AudioRef = _reflection.GeneratedProtocolMessageType('AudioRef', (_message.Message,), dict(
DESCRIPTOR = _AUDIOREF,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.AudioRef)
))
_sym_db.RegisterMessage(AudioRef)
ImageRef = _reflection.GeneratedProtocolMessageType('ImageRef', (_message.Message,), dict(
DESCRIPTOR = _IMAGEREF,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.ImageRef)
))
_sym_db.RegisterMessage(ImageRef)
UploadedUitsId3Tag = _reflection.GeneratedProtocolMessageType('UploadedUitsId3Tag', (_message.Message,), dict(
DESCRIPTOR = _UPLOADEDUITSID3TAG,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadedUitsId3Tag)
))
_sym_db.RegisterMessage(UploadedUitsId3Tag)
AdditionalMetadata = _reflection.GeneratedProtocolMessageType('AdditionalMetadata', (_message.Message,), dict(
DESCRIPTOR = _ADDITIONALMETADATA,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.AdditionalMetadata)
))
_sym_db.RegisterMessage(AdditionalMetadata)
TrackExtras = _reflection.GeneratedProtocolMessageType('TrackExtras', (_message.Message,), dict(
DESCRIPTOR = _TRACKEXTRAS,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.TrackExtras)
))
_sym_db.RegisterMessage(TrackExtras)
Track = _reflection.GeneratedProtocolMessageType('Track', (_message.Message,), dict(
DESCRIPTOR = _TRACK,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.Track)
))
_sym_db.RegisterMessage(Track)
Tracks = _reflection.GeneratedProtocolMessageType('Tracks', (_message.Message,), dict(
DESCRIPTOR = _TRACKS,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.Tracks)
))
_sym_db.RegisterMessage(Tracks)
Playlist = _reflection.GeneratedProtocolMessageType('Playlist', (_message.Message,), dict(
DESCRIPTOR = _PLAYLIST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.Playlist)
))
_sym_db.RegisterMessage(Playlist)
PlaylistEntry = _reflection.GeneratedProtocolMessageType('PlaylistEntry', (_message.Message,), dict(
DESCRIPTOR = _PLAYLISTENTRY,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.PlaylistEntry)
))
_sym_db.RegisterMessage(PlaylistEntry)
TrackSearchRestriction = _reflection.GeneratedProtocolMessageType('TrackSearchRestriction', (_message.Message,), dict(
DESCRIPTOR = _TRACKSEARCHRESTRICTION,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.TrackSearchRestriction)
))
_sym_db.RegisterMessage(TrackSearchRestriction)
TrackSearchRestrictionSet = _reflection.GeneratedProtocolMessageType('TrackSearchRestrictionSet', (_message.Message,), dict(
DESCRIPTOR = _TRACKSEARCHRESTRICTIONSET,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.TrackSearchRestrictionSet)
))
_sym_db.RegisterMessage(TrackSearchRestrictionSet)
TrackSortOrder = _reflection.GeneratedProtocolMessageType('TrackSortOrder', (_message.Message,), dict(
DESCRIPTOR = _TRACKSORTORDER,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.TrackSortOrder)
))
_sym_db.RegisterMessage(TrackSortOrder)
GetTracksRequest = _reflection.GeneratedProtocolMessageType('GetTracksRequest', (_message.Message,), dict(
DESCRIPTOR = _GETTRACKSREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetTracksRequest)
))
_sym_db.RegisterMessage(GetTracksRequest)
GetTracksResponse = _reflection.GeneratedProtocolMessageType('GetTracksResponse', (_message.Message,), dict(
DESCRIPTOR = _GETTRACKSRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetTracksResponse)
))
_sym_db.RegisterMessage(GetTracksResponse)
GetPlaylistEntriesRequest = _reflection.GeneratedProtocolMessageType('GetPlaylistEntriesRequest', (_message.Message,), dict(
DESCRIPTOR = _GETPLAYLISTENTRIESREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetPlaylistEntriesRequest)
))
_sym_db.RegisterMessage(GetPlaylistEntriesRequest)
GetPlaylistEntriesResponse = _reflection.GeneratedProtocolMessageType('GetPlaylistEntriesResponse', (_message.Message,), dict(
DESCRIPTOR = _GETPLAYLISTENTRIESRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetPlaylistEntriesResponse)
))
_sym_db.RegisterMessage(GetPlaylistEntriesResponse)
PlaylistSortOrder = _reflection.GeneratedProtocolMessageType('PlaylistSortOrder', (_message.Message,), dict(
DESCRIPTOR = _PLAYLISTSORTORDER,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.PlaylistSortOrder)
))
_sym_db.RegisterMessage(PlaylistSortOrder)
GetPlaylistsRequest = _reflection.GeneratedProtocolMessageType('GetPlaylistsRequest', (_message.Message,), dict(
DESCRIPTOR = _GETPLAYLISTSREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetPlaylistsRequest)
))
_sym_db.RegisterMessage(GetPlaylistsRequest)
GetPlaylistsResponse = _reflection.GeneratedProtocolMessageType('GetPlaylistsResponse', (_message.Message,), dict(
DESCRIPTOR = _GETPLAYLISTSRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetPlaylistsResponse)
))
_sym_db.RegisterMessage(GetPlaylistsResponse)
LookupTrackRequest = _reflection.GeneratedProtocolMessageType('LookupTrackRequest', (_message.Message,), dict(
DESCRIPTOR = _LOOKUPTRACKREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.LookupTrackRequest)
))
_sym_db.RegisterMessage(LookupTrackRequest)
LookupPlaylistEntryRequest = _reflection.GeneratedProtocolMessageType('LookupPlaylistEntryRequest', (_message.Message,), dict(
DESCRIPTOR = _LOOKUPPLAYLISTENTRYREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.LookupPlaylistEntryRequest)
))
_sym_db.RegisterMessage(LookupPlaylistEntryRequest)
LookupPlaylistRequest = _reflection.GeneratedProtocolMessageType('LookupPlaylistRequest', (_message.Message,), dict(
DESCRIPTOR = _LOOKUPPLAYLISTREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.LookupPlaylistRequest)
))
_sym_db.RegisterMessage(LookupPlaylistRequest)
BatchLookupRequest = _reflection.GeneratedProtocolMessageType('BatchLookupRequest', (_message.Message,), dict(
DESCRIPTOR = _BATCHLOOKUPREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.BatchLookupRequest)
))
_sym_db.RegisterMessage(BatchLookupRequest)
BatchLookupResponse = _reflection.GeneratedProtocolMessageType('BatchLookupResponse', (_message.Message,), dict(
DESCRIPTOR = _BATCHLOOKUPRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.BatchLookupResponse)
))
_sym_db.RegisterMessage(BatchLookupResponse)
MutateTrackRequest = _reflection.GeneratedProtocolMessageType('MutateTrackRequest', (_message.Message,), dict(
DESCRIPTOR = _MUTATETRACKREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.MutateTrackRequest)
))
_sym_db.RegisterMessage(MutateTrackRequest)
MutateResponse = _reflection.GeneratedProtocolMessageType('MutateResponse', (_message.Message,), dict(
DESCRIPTOR = _MUTATERESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.MutateResponse)
))
_sym_db.RegisterMessage(MutateResponse)
BatchMutateTracksRequest = _reflection.GeneratedProtocolMessageType('BatchMutateTracksRequest', (_message.Message,), dict(
DESCRIPTOR = _BATCHMUTATETRACKSREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.BatchMutateTracksRequest)
))
_sym_db.RegisterMessage(BatchMutateTracksRequest)
BatchMutateTracksResponse = _reflection.GeneratedProtocolMessageType('BatchMutateTracksResponse', (_message.Message,), dict(
DESCRIPTOR = _BATCHMUTATETRACKSRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.BatchMutateTracksResponse)
))
_sym_db.RegisterMessage(BatchMutateTracksResponse)
MutatePlaylistRequest = _reflection.GeneratedProtocolMessageType('MutatePlaylistRequest', (_message.Message,), dict(
DESCRIPTOR = _MUTATEPLAYLISTREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.MutatePlaylistRequest)
))
_sym_db.RegisterMessage(MutatePlaylistRequest)
BatchMutatePlaylistsRequest = _reflection.GeneratedProtocolMessageType('BatchMutatePlaylistsRequest', (_message.Message,), dict(
DESCRIPTOR = _BATCHMUTATEPLAYLISTSREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.BatchMutatePlaylistsRequest)
))
_sym_db.RegisterMessage(BatchMutatePlaylistsRequest)
BatchMutatePlaylistsResponse = _reflection.GeneratedProtocolMessageType('BatchMutatePlaylistsResponse', (_message.Message,), dict(
DESCRIPTOR = _BATCHMUTATEPLAYLISTSRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.BatchMutatePlaylistsResponse)
))
_sym_db.RegisterMessage(BatchMutatePlaylistsResponse)
MutatePlaylistEntryRequest = _reflection.GeneratedProtocolMessageType('MutatePlaylistEntryRequest', (_message.Message,), dict(
DESCRIPTOR = _MUTATEPLAYLISTENTRYREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.MutatePlaylistEntryRequest)
))
_sym_db.RegisterMessage(MutatePlaylistEntryRequest)
BatchMutatePlaylistEntriesRequest = _reflection.GeneratedProtocolMessageType('BatchMutatePlaylistEntriesRequest', (_message.Message,), dict(
DESCRIPTOR = _BATCHMUTATEPLAYLISTENTRIESREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.BatchMutatePlaylistEntriesRequest)
))
_sym_db.RegisterMessage(BatchMutatePlaylistEntriesRequest)
BatchMutatePlaylistEntriesResponse = _reflection.GeneratedProtocolMessageType('BatchMutatePlaylistEntriesResponse', (_message.Message,), dict(
DESCRIPTOR = _BATCHMUTATEPLAYLISTENTRIESRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.BatchMutatePlaylistEntriesResponse)
))
_sym_db.RegisterMessage(BatchMutatePlaylistEntriesResponse)
MagicPlaylistSeed = _reflection.GeneratedProtocolMessageType('MagicPlaylistSeed', (_message.Message,), dict(
DESCRIPTOR = _MAGICPLAYLISTSEED,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.MagicPlaylistSeed)
))
_sym_db.RegisterMessage(MagicPlaylistSeed)
MagicPlaylistRequest = _reflection.GeneratedProtocolMessageType('MagicPlaylistRequest', (_message.Message,), dict(
DESCRIPTOR = _MAGICPLAYLISTREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.MagicPlaylistRequest)
))
_sym_db.RegisterMessage(MagicPlaylistRequest)
MagicPlaylistResponse = _reflection.GeneratedProtocolMessageType('MagicPlaylistResponse', (_message.Message,), dict(
DESCRIPTOR = _MAGICPLAYLISTRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.MagicPlaylistResponse)
))
_sym_db.RegisterMessage(MagicPlaylistResponse)
FlushLockerRequest = _reflection.GeneratedProtocolMessageType('FlushLockerRequest', (_message.Message,), dict(
DESCRIPTOR = _FLUSHLOCKERREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.FlushLockerRequest)
))
_sym_db.RegisterMessage(FlushLockerRequest)
FlushLockerResponse = _reflection.GeneratedProtocolMessageType('FlushLockerResponse', (_message.Message,), dict(
DESCRIPTOR = _FLUSHLOCKERRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.FlushLockerResponse)
))
_sym_db.RegisterMessage(FlushLockerResponse)
LockerNotification = _reflection.GeneratedProtocolMessageType('LockerNotification', (_message.Message,), dict(
DESCRIPTOR = _LOCKERNOTIFICATION,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.LockerNotification)
))
_sym_db.RegisterMessage(LockerNotification)
Album = _reflection.GeneratedProtocolMessageType('Album', (_message.Message,), dict(
DESCRIPTOR = _ALBUM,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.Album)
))
_sym_db.RegisterMessage(Album)
AlbumSortOrder = _reflection.GeneratedProtocolMessageType('AlbumSortOrder', (_message.Message,), dict(
DESCRIPTOR = _ALBUMSORTORDER,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.AlbumSortOrder)
))
_sym_db.RegisterMessage(AlbumSortOrder)
GetAlbumsRequest = _reflection.GeneratedProtocolMessageType('GetAlbumsRequest', (_message.Message,), dict(
DESCRIPTOR = _GETALBUMSREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetAlbumsRequest)
))
_sym_db.RegisterMessage(GetAlbumsRequest)
GetAlbumsResponse = _reflection.GeneratedProtocolMessageType('GetAlbumsResponse', (_message.Message,), dict(
DESCRIPTOR = _GETALBUMSRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetAlbumsResponse)
))
_sym_db.RegisterMessage(GetAlbumsResponse)
Artist = _reflection.GeneratedProtocolMessageType('Artist', (_message.Message,), dict(
DESCRIPTOR = _ARTIST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.Artist)
))
_sym_db.RegisterMessage(Artist)
ArtistSortOrder = _reflection.GeneratedProtocolMessageType('ArtistSortOrder', (_message.Message,), dict(
DESCRIPTOR = _ARTISTSORTORDER,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.ArtistSortOrder)
))
_sym_db.RegisterMessage(ArtistSortOrder)
GetArtistsRequest = _reflection.GeneratedProtocolMessageType('GetArtistsRequest', (_message.Message,), dict(
DESCRIPTOR = _GETARTISTSREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetArtistsRequest)
))
_sym_db.RegisterMessage(GetArtistsRequest)
GetArtistsResponse = _reflection.GeneratedProtocolMessageType('GetArtistsResponse', (_message.Message,), dict(
DESCRIPTOR = _GETARTISTSRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetArtistsResponse)
))
_sym_db.RegisterMessage(GetArtistsResponse)
MusicGenre = _reflection.GeneratedProtocolMessageType('MusicGenre', (_message.Message,), dict(
DESCRIPTOR = _MUSICGENRE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.MusicGenre)
))
_sym_db.RegisterMessage(MusicGenre)
GenreSortOrder = _reflection.GeneratedProtocolMessageType('GenreSortOrder', (_message.Message,), dict(
DESCRIPTOR = _GENRESORTORDER,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GenreSortOrder)
))
_sym_db.RegisterMessage(GenreSortOrder)
GetGenresRequest = _reflection.GeneratedProtocolMessageType('GetGenresRequest', (_message.Message,), dict(
DESCRIPTOR = _GETGENRESREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetGenresRequest)
))
_sym_db.RegisterMessage(GetGenresRequest)
GetGenresResponse = _reflection.GeneratedProtocolMessageType('GetGenresResponse', (_message.Message,), dict(
DESCRIPTOR = _GETGENRESRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetGenresResponse)
))
_sym_db.RegisterMessage(GetGenresResponse)
GetDynamicPlaylistEntriesRequest = _reflection.GeneratedProtocolMessageType('GetDynamicPlaylistEntriesRequest', (_message.Message,), dict(
DESCRIPTOR = _GETDYNAMICPLAYLISTENTRIESREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetDynamicPlaylistEntriesRequest)
))
_sym_db.RegisterMessage(GetDynamicPlaylistEntriesRequest)
GetDynamicPlaylistEntriesResponse = _reflection.GeneratedProtocolMessageType('GetDynamicPlaylistEntriesResponse', (_message.Message,), dict(
DESCRIPTOR = _GETDYNAMICPLAYLISTENTRIESRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetDynamicPlaylistEntriesResponse)
))
_sym_db.RegisterMessage(GetDynamicPlaylistEntriesResponse)
GetAggregationsByTrackTypeRequest = _reflection.GeneratedProtocolMessageType('GetAggregationsByTrackTypeRequest', (_message.Message,), dict(
DESCRIPTOR = _GETAGGREGATIONSBYTRACKTYPEREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetAggregationsByTrackTypeRequest)
))
_sym_db.RegisterMessage(GetAggregationsByTrackTypeRequest)
TrackTypeAggregate = _reflection.GeneratedProtocolMessageType('TrackTypeAggregate', (_message.Message,), dict(
DESCRIPTOR = _TRACKTYPEAGGREGATE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.TrackTypeAggregate)
))
_sym_db.RegisterMessage(TrackTypeAggregate)
GetAggregationsByTrackTypeResponse = _reflection.GeneratedProtocolMessageType('GetAggregationsByTrackTypeResponse', (_message.Message,), dict(
DESCRIPTOR = _GETAGGREGATIONSBYTRACKTYPERESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetAggregationsByTrackTypeResponse)
))
_sym_db.RegisterMessage(GetAggregationsByTrackTypeResponse)
GetAggregationsByAvailabilityStatusRequest = _reflection.GeneratedProtocolMessageType('GetAggregationsByAvailabilityStatusRequest', (_message.Message,), dict(
DESCRIPTOR = _GETAGGREGATIONSBYAVAILABILITYSTATUSREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetAggregationsByAvailabilityStatusRequest)
))
_sym_db.RegisterMessage(GetAggregationsByAvailabilityStatusRequest)
AvailabilityStatusAggregate = _reflection.GeneratedProtocolMessageType('AvailabilityStatusAggregate', (_message.Message,), dict(
DESCRIPTOR = _AVAILABILITYSTATUSAGGREGATE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.AvailabilityStatusAggregate)
))
_sym_db.RegisterMessage(AvailabilityStatusAggregate)
GetAggregationsByAvailabilityStatusResponse = _reflection.GeneratedProtocolMessageType('GetAggregationsByAvailabilityStatusResponse', (_message.Message,), dict(
DESCRIPTOR = _GETAGGREGATIONSBYAVAILABILITYSTATUSRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetAggregationsByAvailabilityStatusResponse)
))
_sym_db.RegisterMessage(GetAggregationsByAvailabilityStatusResponse)
AddPromoTracksRequest = _reflection.GeneratedProtocolMessageType('AddPromoTracksRequest', (_message.Message,), dict(
DESCRIPTOR = _ADDPROMOTRACKSREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.AddPromoTracksRequest)
))
_sym_db.RegisterMessage(AddPromoTracksRequest)
AddPromoTracksResponse = _reflection.GeneratedProtocolMessageType('AddPromoTracksResponse', (_message.Message,), dict(
DESCRIPTOR = _ADDPROMOTRACKSRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.AddPromoTracksResponse)
))
_sym_db.RegisterMessage(AddPromoTracksResponse)
GetPlaylistAggregationsRequest = _reflection.GeneratedProtocolMessageType('GetPlaylistAggregationsRequest', (_message.Message,), dict(
DESCRIPTOR = _GETPLAYLISTAGGREGATIONSREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetPlaylistAggregationsRequest)
))
_sym_db.RegisterMessage(GetPlaylistAggregationsRequest)
PlaylistAggregate = _reflection.GeneratedProtocolMessageType('PlaylistAggregate', (_message.Message,), dict(
DESCRIPTOR = _PLAYLISTAGGREGATE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.PlaylistAggregate)
))
_sym_db.RegisterMessage(PlaylistAggregate)
GetPlaylistAggregationsResponse = _reflection.GeneratedProtocolMessageType('GetPlaylistAggregationsResponse', (_message.Message,), dict(
DESCRIPTOR = _GETPLAYLISTAGGREGATIONSRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetPlaylistAggregationsResponse)
))
_sym_db.RegisterMessage(GetPlaylistAggregationsResponse)
RemoteControlCommandRequest = _reflection.GeneratedProtocolMessageType('RemoteControlCommandRequest', (_message.Message,), dict(
DESCRIPTOR = _REMOTECONTROLCOMMANDREQUEST,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.RemoteControlCommandRequest)
))
_sym_db.RegisterMessage(RemoteControlCommandRequest)
RemoteControlCommandResponse = _reflection.GeneratedProtocolMessageType('RemoteControlCommandResponse', (_message.Message,), dict(
DESCRIPTOR = _REMOTECONTROLCOMMANDRESPONSE,
__module__ = 'locker_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.RemoteControlCommandResponse)
))
_sym_db.RegisterMessage(RemoteControlCommandResponse)
# @@protoc_insertion_point(module_scope)
gmusicapi-12.1.1/gmusicapi/protocol/mobileclient.py 0000664 0001750 0001750 00000152520 13400371522 022717 0 ustar simon simon 0000000 0000000 #!/usr/bin/env python
# -*- coding: utf-8 -*-
"""Calls made by the mobile client."""
from __future__ import print_function, division, absolute_import, unicode_literals
from six import raise_from
from builtins import * # noqa
import base64
import calendar
import copy
from datetime import datetime
from hashlib import sha1
import hmac
import time
from uuid import uuid1
import validictory
import json
from gmusicapi.exceptions import ValidationException, CallFailure
from gmusicapi.protocol.shared import Call, authtypes
from gmusicapi.utils import utils
# URL for sj service
sj_url = 'https://mclients.googleapis.com/sj/v2.5/'
sj_stream_url = 'https://mclients.googleapis.com/music/'
# shared schemas
sj_image_color_styles = {
'type': 'object',
'additionalProperties': False,
'properties': {
'primary': {'type': 'object',
'additionalProperties': False,
'properties': {
'red': {'type': 'integer'},
'green': {'type': 'integer'},
'blue': {'type': 'integer'}
}},
'scrim': {'type': 'object',
'additionalProperties': False,
'properties': {
'red': {'type': 'integer'},
'green': {'type': 'integer'},
'blue': {'type': 'integer'}
}},
'accent': {'type': 'object',
'additionalProperties': False,
'properties': {
'red': {'type': 'integer'},
'green': {'type': 'integer'},
'blue': {'type': 'integer'}
}}
}
}
sj_image = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'url': {'type': 'string'},
'aspectRatio': {'type': 'string',
'required': False},
'autogen': {'type': 'boolean',
'required': False},
'colorStyles': sj_image_color_styles.copy()
}
}
sj_image['properties']['colorStyles']['required'] = False
sj_video = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'id': {'type': 'string'},
'title': {'type': 'string', 'required': False},
'thumbnails': {'type': 'array',
'required': False,
'items': {'type': 'object', 'properties': {
'url': {'type': 'string'},
'width': {'type': 'integer'},
'height': {'type': 'integer'},
}}},
}
}
sj_track = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'title': {'type': 'string'},
'artist': {'type': 'string'},
'album': {'type': 'string'},
'albumArtist': {'type': 'string', 'blank': True},
'trackNumber': {'type': 'integer'},
'totalTrackCount': {'type': 'integer', 'required': False},
'durationMillis': {'type': 'string'},
'albumArtRef': {'type': 'array',
'items': {'type': 'object', 'properties': {'url': {'type': 'string'}}},
'required': False},
'artistArtRef': {'type': 'array',
'items': {'type': 'object', 'properties': {'url': {'type': 'string'}}},
'required': False,
},
'discNumber': {'type': 'integer'},
'totalDiscCount': {'type': 'integer', 'required': False},
'estimatedSize': {'type': 'string', 'required': False},
'trackType': {'type': 'string', 'required': False},
'storeId': {'type': 'string', 'required': False},
'albumId': {'type': 'string'},
'artistId': {'type': 'array',
'items': {'type': 'string', 'blank': True}, 'required': False},
'nid': {'type': 'string', 'required': False},
'trackAvailableForPurchase': {'type': 'boolean', 'required': False},
'albumAvailableForPurchase': {'type': 'boolean', 'required': False},
'composer': {'type': 'string', 'blank': True},
'playCount': {'type': 'integer', 'required': False},
'year': {'type': 'integer', 'required': False},
'rating': {'type': 'string', 'required': False},
'genre': {'type': 'string', 'required': False},
'trackAvailableForSubscription': {'type': 'boolean', 'required': False},
# Only available when rating differs from '0'
# when using :change_song_metadata:, specifying this value will cause all clients to
# properly update (web/mobile). As value int(round(time.time() * 1000000)) works quite well
'lastRatingChangeTimestamp': {'type': 'string', 'required': False},
'primaryVideo': sj_video.copy(),
'lastModifiedTimestamp': {'type': 'string', 'required': False},
'explicitType': {'type': 'string', 'required': False},
'contentType': {'type': 'string', 'required': False},
'deleted': {'type': 'boolean', 'required': False},
'creationTimestamp': {'type': 'string', 'required': False},
'comment': {'type': 'string', 'required': False, 'blank': True},
'beatsPerMinute': {'type': 'integer', 'required': False},
'recentTimestamp': {'type': 'string', 'required': False},
'clientId': {'type': 'string', 'required': False},
'id': {'type': 'string', 'required': False}
}
}
sj_track['properties']['primaryVideo']['required'] = False
sj_playlist = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'name': {'type': 'string'},
'deleted': {'type': 'boolean',
'required': False}, # for public
'type': {'type': 'string',
'pattern': r'MAGIC|SHARED|USER_GENERATED',
'required': False,
},
'lastModifiedTimestamp': {'type': 'string',
'required': False}, # for public
'recentTimestamp': {'type': 'string',
'required': False}, # for public
'shareToken': {'type': 'string'},
'ownerProfilePhotoUrl': {'type': 'string', 'required': False},
'ownerName': {'type': 'string',
'required': False},
'accessControlled': {'type': 'boolean',
'required': False}, # for public
'shareState': {'type': 'string',
'pattern': r'PRIVATE|PUBLIC',
'required': False}, # for public
'creationTimestamp': {'type': 'string',
'required': False}, # for public
'id': {'type': 'string',
'required': False}, # for public
'albumArtRef': {'type': 'array',
'items': {'type': 'object', 'properties': {'url': {'type': 'string'}}},
'required': False,
},
'description': {'type': 'string',
'blank': True,
'required': False},
'explicitType': {'type': 'string', 'required': False},
'contentType': {'type': 'string', 'required': False}
}
}
sj_plentry = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'id': {'type': 'string'},
'clientId': {'type': 'string'},
'playlistId': {'type': 'string'},
'absolutePosition': {'type': 'string'},
'trackId': {'type': 'string'},
'creationTimestamp': {'type': 'string'},
'lastModifiedTimestamp': {'type': 'string'},
'deleted': {'type': 'boolean'},
'source': {'type': 'string'},
'track': sj_track.copy()
},
}
sj_plentry['properties']['track']['required'] = False
sj_attribution = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'license_url': {'type': 'string', 'required': False},
'license_title': {'type': 'string', 'required': False},
'source_title': {'type': 'string', 'blank': True, 'required': False},
'source_url': {'type': 'string', 'blank': True, 'required': False},
}
}
sj_album = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'name': {'type': 'string'},
'albumArtist': {'type': 'string'},
'albumArtRef': {'type': 'string', 'required': False},
'albumId': {'type': 'string'},
'artist': {'type': 'string', 'blank': True},
'artistId': {'type': 'array', 'items': {'type': 'string', 'blank': True}},
'year': {'type': 'integer', 'required': False},
'tracks': {'type': 'array', 'items': sj_track, 'required': False},
'description': {'type': 'string', 'required': False},
'description_attribution': sj_attribution.copy(),
'explicitType': {'type': 'string', 'required': False},
'contentType': {'type': 'string', 'required': False}
}
}
sj_album['properties']['description_attribution']['required'] = False
sj_artist = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'name': {'type': 'string'},
'artistArtRef': {'type': 'string', 'required': False},
'artistArtRefs': {'type': 'array',
'items': sj_image,
'required': False},
'artistBio': {'type': 'string', 'required': False},
'artistId': {'type': 'string', 'blank': True, 'required': False},
'albums': {'type': 'array', 'items': sj_album, 'required': False},
'topTracks': {'type': 'array', 'items': sj_track, 'required': False},
'total_albums': {'type': 'integer', 'required': False},
'artist_bio_attribution': sj_attribution.copy(),
}
}
sj_artist['properties']['artist_bio_attribution']['required'] = False
sj_artist['properties']['related_artists'] = {
'type': 'array',
'items': sj_artist, # note the recursion
'required': False
}
sj_genre = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'id': {'type': 'string'},
'name': {'type': 'string'},
'children': {
'type': 'array',
'required': False,
'items': {'type': 'string'}
},
'parentId': {
'type': 'string',
'required': False,
},
'images': {
'type': 'array',
'required': False,
'items': {
'type': 'object',
'additionalProperties': False,
'properties': {
'url': {'type': 'string'}
}
}
}
}
}
sj_station_metadata_seed = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
# one of these will be present
'artist': {
'type': sj_artist,
'required': False
},
'genre': {
'type': sj_genre,
'required': False
},
}
}
sj_station_seed = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'seedType': {'type': 'string'},
# one of these will be present
'albumId': {'type': 'string', 'required': False},
'artistId': {'type': 'string', 'required': False},
'genreId': {'type': 'string', 'required': False},
'trackId': {'type': 'string', 'required': False},
'trackLockerId': {'type': 'string', 'required': False},
'curatedStationId': {'type': 'string', 'required': False},
'metadataSeed': {'type': sj_station_metadata_seed, 'required': False}
}
}
sj_station_track = sj_track.copy()
sj_station_track['properties']['wentryid'] = {'type': 'string', 'required': False}
sj_station = {
'type': 'object',
'additionalProperties': False,
'properties': {
'imageUrl': {'type': 'string', 'required': False},
'kind': {'type': 'string'},
'name': {'type': 'string'},
'deleted': {'type': 'boolean',
'required': False}, # for public
'lastModifiedTimestamp': {'type': 'string',
'required': False},
'recentTimestamp': {'type': 'string',
'required': False}, # for public
'clientId': {'type': 'string',
'required': False}, # for public
'sessionToken': {'type': 'string',
'required': False}, # for free radios
'skipEventHistory': {'type': 'array'}, # TODO: What's in this array?
'seed': sj_station_seed,
'stationSeeds': {'type': 'array',
'items': sj_station_seed},
'id': {'type': 'string',
'required': False}, # for public
'description': {'type': 'string', 'required': False},
'tracks': {'type': 'array', 'required': False, 'items': sj_station_track},
'imageUrls': {'type': 'array',
'required': False,
'items': sj_image
},
'compositeArtRefs': {'type': 'array',
'required': False,
'items': sj_image
},
'contentTypes': {'type': 'array',
'required': False,
'items': {'type': 'string'}
},
'byline': {'type': 'string', 'required': False},
'adTargeting': {
'type': 'object',
'properties': {
'keyword': {'type': 'array',
'items': {'type': 'string'}
},
},
'required': False,
},
}
}
sj_listen_now_album = {
'type': 'object',
'additionalProperties': False,
'properties': {
'artist_metajam_id': {'type': 'string'},
'artist_name': {'type': 'string'},
'artist_profile_image': {
'type': 'object',
'url': {'type': 'string'}
},
'description': {
'type': 'string',
'blank': True
},
'description_attribution': {
'type': sj_attribution,
'required': False
},
'explicitType': {'type': 'string', 'required': False},
'id': {
'type': 'object',
'properties': {
'metajamCompactKey': {'type': 'string'},
'artist': {'type': 'string'},
'title': {'type': 'string'}
}
},
'title': {'type': 'string'}
}
}
sj_listen_now_station = {
'type': 'object',
'additionalProperties': False,
'properties': {
'highlight_color': {
'type': 'string',
'required': False
},
'id': {
'type': 'object',
'seeds': {
'type': 'array',
'items': {'type': sj_station_seed}
}
},
'profile_image': {
'type': 'object',
'required': False,
'url': {'type': 'string'}
},
'title': {'type': 'string'}
}
}
sj_listen_now_item = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'compositeArtRefs': {
'type': 'array',
'required': False,
'items': {'type': sj_image}
},
'images': {
'type': 'array',
'items': {'type': sj_image},
'required': False,
},
'suggestion_reason': {'type': 'string'},
'suggestion_text': {'type': 'string'},
'type': {'type': 'string'},
'album': {
'type': sj_listen_now_album,
'required': False
},
'radio_station': {
'type': sj_listen_now_station,
'required': False
}
}
}
sj_podcast_genre = {
'type': 'object',
'additionalProperties': False,
'properties': {
'id': {'type': 'string'},
'displayName': {'type': 'string'}
}
}
sj_podcast_genre['properties']['subgroups'] = {
'type': 'array',
'required': False,
'items': sj_podcast_genre
}
sj_podcast_episode = {
'type': 'object',
'additionalProperties': False,
'properties': {
'art': {
'type': 'array',
'required': False,
'items': sj_image
},
'author': {
'type': 'string',
'required': False
},
'deleted': {
'type': 'string',
'required': False
},
'description': {
'type': 'string',
'required': False
},
'durationMillis': {'type': 'string'},
'episodeId': {'type': 'string'},
'explicitType': {'type': 'string'},
'fileSize': {'type': 'string'},
'playbackPositionMillis': {
'type': 'string',
'required': False
},
'publicationTimestampMillis': {
'type': 'string',
'required': False
},
'seriesId': {'type': 'string'},
'seriesTitle': {'type': 'string'},
'title': {'type': 'string'}
},
}
sj_podcast_series = {
'type': 'object',
'additionalProperties': False,
'properties': {
'art': {
'type': 'array',
'required': False,
'items': sj_image
},
'author': {'type': 'string'},
'continuationToken': {
'type': 'string',
'required': False,
'blank': True
},
'copyright': {
'type': 'string',
'required': False
},
'description': {
'type': 'string',
'required': False
},
'episodes': {
'type': 'array',
'required': False,
'items': sj_podcast_episode
},
'explicitType': {'type': 'string'},
'link': {
'type': 'string',
'required': False
},
'seriesId': {'type': 'string'},
'title': {'type': 'string'},
'totalNumEpisodes': {'type': 'integer'},
'userPreferences': {
'type': 'object',
'required': False,
'properties': {
'autoDownload': {
'type': 'boolean',
'required': False
},
'notifyOnNewEpisode': {
'type': 'boolean',
'required': False
},
'subscribed': {'type': 'boolean'}
}
}
}
}
sj_situation = {
'type': 'object',
'additionalProperties': False,
'properties': {
'description': {'type': 'string'},
'id': {'type': 'string'},
'imageUrl': {'type': 'string', 'required': False},
'title': {'type': 'string'},
'wideImageUrl': {'type': 'string', 'required': False},
'stations': {'type': 'array',
'required': False,
'items': sj_station
}
}
}
sj_situation['properties']['situations'] = {
'type': 'array',
'required': False,
'items': sj_situation
}
sj_search_result_cluster_info = {
'type': 'object',
'additionalProperties': False,
'properties': {
'category': {'type': 'string'},
'id': {'type': 'string'},
'type': {'type': 'string'}
}
}
sj_search_result = {
'type': 'object',
'additionalProperties': False,
'properties': {
'score': {'type': 'number', 'required': False},
'type': {'type': 'string'},
'best_result': {'type': 'boolean', 'required': False},
'navigational_result': {'type': 'boolean', 'required': False},
'navigational_confidence': {'type': 'number', 'required': False},
'cluster': {
'type': 'array',
'required': False,
'items': sj_search_result_cluster_info
},
'artist': sj_artist.copy(),
'album': sj_album.copy(),
'track': sj_track.copy(),
'playlist': sj_playlist.copy(),
'genre': sj_genre.copy(),
'series': sj_podcast_series.copy(),
'station': sj_station.copy(),
'situation': sj_situation.copy(),
'youtube_video': sj_video.copy()
}
}
sj_search_result['properties']['artist']['required'] = False
sj_search_result['properties']['album']['required'] = False
sj_search_result['properties']['track']['required'] = False
sj_search_result['properties']['playlist']['required'] = False
sj_search_result['properties']['genre']['required'] = False
sj_search_result['properties']['series']['required'] = False
sj_search_result['properties']['station']['required'] = False
sj_search_result['properties']['situation']['required'] = False
sj_search_result['properties']['youtube_video']['required'] = False
sj_search_result_cluster = {
'type': 'object',
'additionalProperties': False,
'properties': {
'cluster': {'type': sj_search_result_cluster_info},
'displayName': {'type': 'string', 'required': False},
'entries': {
'type': 'array',
'items': sj_search_result,
'required': False
},
'resultToken': {'type': 'string', 'required': False}
}
}
class McCall(Call):
"""Abstract base for mobile client calls."""
required_auth = authtypes(gpsoauth=True)
# validictory schema for the response
_res_schema = utils.NotImplementedField
@classmethod
def validate(cls, response, msg):
"""Use validictory and a static schema (stored in cls._res_schema)."""
try:
return validictory.validate(msg, cls._res_schema)
except ValueError as e:
raise_from(ValidationException(str(e)), e)
@classmethod
def check_success(cls, response, msg):
# TODO not sure if this is still valid for mc
pass
# if 'success' in msg and not msg['success']:
# raise CallFailure(
# "the server reported failure. This is usually"
# " caused by bad arguments, but can also happen if requests"
# " are made too quickly (eg creating a playlist then"
# " modifying it before the server has created it)",
# cls.__name__)
@classmethod
def parse_response(cls, response):
return cls._parse_json(response.text)
class McListCall(McCall):
"""Abc for calls that list a resource."""
# concrete classes provide:
item_schema = utils.NotImplementedField
filter_text = utils.NotImplementedField
static_headers = {'Content-Type': 'application/json'}
static_params = {'alt': 'json', 'include-tracks': 'true'}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'nextPageToken': {'type': 'string', 'required': False},
'data': {'type': 'object',
'items': {'type': 'array', 'items': item_schema},
'required': False,
},
},
}
@classmethod
def dynamic_params(cls, updated_after=None, start_token=None, max_results=None):
"""
:param updated_after: datetime.datetime; defaults to epoch
"""
if updated_after is None:
microseconds = -1
else:
microseconds = utils.datetime_to_microseconds(updated_after)
return {'updated-min': microseconds}
@classmethod
def dynamic_data(cls, updated_after=None, start_token=None, max_results=None):
"""
:param updated_after: ignored
:param start_token: nextPageToken from a previous response
:param max_results: a positive int; if not provided, server defaults to 1000
"""
data = {}
if start_token is not None:
data['start-token'] = start_token
if max_results is not None:
data['max-results'] = str(max_results)
return json.dumps(data)
@classmethod
def parse_response(cls, response):
# empty results don't include the data key
# make sure it's always there
res = cls._parse_json(response.text)
if 'data' not in res:
res['data'] = {'items': []}
return res
@classmethod
def filter_response(cls, msg):
filtered = copy.deepcopy(msg)
filtered['data']['items'] = ["<%s %s>" % (len(filtered['data']['items']),
cls.filter_text)]
return filtered
class McBatchMutateCall(McCall):
"""Abc for batch mutation calls."""
static_headers = {'Content-Type': 'application/json'}
static_params = {'alt': 'json'}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'mutate_response': {
'type': 'array',
'items': {
'type': 'object',
'properties': {
'id': {'type': 'string', 'required': False},
'client_id': {'type': 'string', 'blank': True,
'required': False},
'response_code': {'type': 'string'},
},
},
}
},
}
@staticmethod
def dynamic_data(mutations):
"""
:param mutations: list of mutation dictionaries
"""
return json.dumps({'mutations': mutations})
@classmethod
def check_success(cls, response, msg):
if ('error' in msg or
not all([d.get('response_code', None) in ('OK', 'CONFLICT')
for d in msg.get('mutate_response', [])])):
raise CallFailure('The server reported failure while'
' changing the requested resource.'
" If this wasn't caused by invalid arguments"
' or server flakiness,'
' please open an issue.',
cls.__name__)
class McStreamCall(McCall):
# this call will redirect to the mp3
static_allow_redirects = False
_s1 = bytes(base64.b64decode('VzeC4H4h+T2f0VI180nVX8x+Mb5HiTtGnKgH52Otj8ZCGDz9jRW'
'yHb6QXK0JskSiOgzQfwTY5xgLLSdUSreaLVMsVVWfxfa8Rw=='))
_s2 = bytes(base64.b64decode('ZAPnhUkYwQ6y5DdQxWThbvhJHN8msQ1rqJw0ggKdufQjelrKuiG'
'GJI30aswkgCWTDyHkTGK9ynlqTkJ5L4CiGGUabGeo8M6JTQ=='))
# bitwise and of _s1 and _s2 ascii, converted to string
_key = ''.join([chr(c1 ^ c2) for (c1, c2) in zip(_s1, _s2)]).encode("ascii")
@classmethod
def get_signature(cls, item_id, salt=None):
"""Return a (sig, salt) pair for url signing."""
if salt is None:
salt = str(int(time.time() * 1000))
mac = hmac.new(cls._key, item_id.encode("utf-8"), sha1)
mac.update(salt.encode("utf-8"))
sig = base64.urlsafe_b64encode(mac.digest())[:-1]
return sig, salt
@staticmethod
def dynamic_headers(item_id, device_id, quality):
return {'X-Device-ID': device_id}
@classmethod
def dynamic_params(cls, item_id, device_id, quality):
sig, salt = cls.get_signature(item_id)
params = {'opt': quality,
'net': 'mob',
'pt': 'e',
'slt': salt,
'sig': sig,
}
if item_id.startswith(('T', 'D')):
# Store track or podcast episode.
params['mjck'] = item_id
else:
# Library track.
params['songid'] = item_id
return params
@staticmethod
def parse_response(response):
return response.headers['location'] # ie where we were redirected
@classmethod
def validate(cls, response, msg):
pass
class Config(McCall):
static_method = 'GET'
static_url = sj_url + 'config'
item_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'key': {'type': 'string'},
'value': {'type': 'string'}
}
}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'data': {'type': 'object',
'entries': {'type': 'array', 'items': item_schema},
}
}
}
class Search(McCall):
"""Search for All Access tracks."""
static_method = 'GET'
static_url = sj_url + 'query'
# The result types returned are requested in the `ct` parameter.
# Free account search is restricted so may not contain hits for all result types.
# 1: Song, 2: Artist, 3: Album, 4: Playlist, 5: Genre,
# 6: Station, 7: Situation, 8: Video, 9: Podcast Series
static_params = {'ct': '1,2,3,4,5,6,7,8,9', 'ic': True}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'clusterDetail': {'type': 'array',
'items': {'type': sj_search_result_cluster}},
'suggestedQuery': {'type': 'string', 'required': False}
},
}
@staticmethod
def dynamic_params(query, max_results):
return {'q': query, 'max-results': max_results}
class ListTracks(McListCall):
item_schema = sj_track
filter_text = 'tracks'
static_method = 'POST'
static_url = sj_url + 'trackfeed'
class GetStreamUrl(McStreamCall):
static_method = 'GET'
static_url = sj_stream_url + 'mplay'
class GetStationTrackStreamUrl(McStreamCall):
static_method = 'GET'
static_url = sj_stream_url + 'wplay'
@staticmethod
def dynamic_headers(item_id, wentry_id, session_token, quality):
return {'X-Device-ID': ''}
@classmethod
def dynamic_params(cls, song_id, wentry_id, session_token, quality):
sig, salt = cls.get_signature(song_id)
params = {}
if song_id[0] == 'T':
# all access
params['mjck'] = song_id
else:
params['songid'] = song_id
params['sesstok'] = session_token.encode('utf-8')
params['wentryid'] = wentry_id.encode('utf-8')
params['tier'] = 'fr'
params.update(
{'audio_formats': 'mp3',
'opt': quality,
'net': 'mob',
'pt': 'a',
'slt': salt,
'sig': sig,
})
return params
@staticmethod
def parse_response(response):
res = json.loads(response.text)
return res['location']
class ListPlaylists(McListCall):
item_schema = sj_playlist
filter_text = 'playlists'
static_method = 'POST'
static_url = sj_url + 'playlistfeed'
class ListPlaylistEntries(McListCall):
item_schema = sj_plentry
filter_text = 'plentries'
static_method = 'POST'
static_url = sj_url + 'plentryfeed'
class ListSharedPlaylistEntries(McListCall):
shared_plentry = sj_plentry.copy()
del shared_plentry['properties']['playlistId']
del shared_plentry['properties']['clientId']
item_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'shareToken': {'type': 'string'},
'responseCode': {'type': 'string'},
'playlistEntry': {
'type': 'array',
'items': shared_plentry,
'required': False,
}
}
}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'entries': {'type': 'array',
'items': item_schema,
},
},
}
filter_text = 'shared plentries'
static_method = 'POST'
static_url = sj_url + 'plentries/shared'
# odd: this request has an additional level of nesting compared to others,
# and changes the data/entry schema to entries/playlistEntry.
# Those horrible naming choices make this even harder to understand.
@classmethod
def dynamic_params(cls, share_token, updated_after=None, start_token=None, max_results=None):
return super(ListSharedPlaylistEntries, cls).dynamic_params(
updated_after, start_token, max_results)
@classmethod
def dynamic_data(cls, share_token, updated_after=None, start_token=None, max_results=None):
"""
:param share_token: from a shared playlist
:param updated_after: ignored
:param start_token: nextPageToken from a previous response
:param max_results: a positive int; if not provided, server defaults to 1000
"""
data = {}
data['shareToken'] = share_token
if start_token is not None:
data['start-token'] = start_token
if max_results is not None:
data['max-results'] = str(max_results)
return json.dumps({'entries': [data]})
@classmethod
def parse_response(cls, response):
res = cls._parse_json(response.text)
if 'playlistEntry' not in res['entries'][0]:
res['entries'][0]['playlistEntry'] = []
return res
@classmethod
def filter_response(cls, msg):
filtered = copy.deepcopy(msg)
filtered['entries'][0]['playlistEntry'] = ["<%s %s>" %
(len(filtered['entries'][0]['playlistEntry']),
cls.filter_text)]
return filtered
class BatchMutatePlaylists(McBatchMutateCall):
static_method = 'POST'
static_url = sj_url + 'playlistbatch'
@staticmethod
def build_playlist_deletes(playlist_ids):
# TODO can probably pull this up one
"""
:param playlist_ids:
"""
return [{'delete': id} for id in playlist_ids]
@staticmethod
def build_playlist_updates(pl_updates):
"""
:param pl_updates: [{'id': '', name': '', 'description': '', 'public': ''}]
"""
return [{'update': {
'id': pl_update['id'],
'name': pl_update['name'],
'description': pl_update['description'],
'shareState': pl_update['public']
}} for pl_update in pl_updates]
@staticmethod
def build_playlist_adds(pl_descriptions):
"""
:param pl_descriptions: [{'name': '', 'description': '','public': ''}]
"""
return [{'create': {
'creationTimestamp': '-1',
'deleted': False,
'lastModifiedTimestamp': '0',
'name': pl_desc['name'],
'description': pl_desc['description'],
'type': 'USER_GENERATED',
'shareState': pl_desc['public'],
}} for pl_desc in pl_descriptions]
class BatchMutatePlaylistEntries(McBatchMutateCall):
filter_text = 'plentries'
item_schema = sj_plentry
static_method = 'POST'
static_url = sj_url + 'plentriesbatch'
@staticmethod
def build_plentry_deletes(entry_ids):
"""
:param entry_ids:
"""
return [{'delete': id} for id in entry_ids]
@staticmethod
def build_plentry_reorder(plentry, preceding_cid, following_cid):
"""
:param plentry: plentry that is moving
:param preceding_cid: clientid of entry that will be before the moved entry
:param following_cid: "" that will be after the moved entry
"""
mutation = copy.deepcopy(plentry)
keys_to_keep = {'clientId', 'creationTimestamp', 'deleted', 'id', 'lastModifiedTimestamp',
'playlistId', 'source', 'trackId'}
for key in list(mutation.keys()):
if key not in keys_to_keep:
del mutation[key]
# horribly misleading key names; these are _clientids_
# using entryids works sometimes, but with seemingly random results
if preceding_cid:
mutation['precedingEntryId'] = preceding_cid
if following_cid:
mutation['followingEntryId'] = following_cid
return {'update': mutation}
@staticmethod
def build_plentry_adds(playlist_id, song_ids):
"""
:param playlist_id:
:param song_ids:
"""
mutations = []
prev_id, cur_id, next_id = None, str(uuid1()), str(uuid1())
for i, song_id in enumerate(song_ids):
m_details = {
'clientId': cur_id,
'creationTimestamp': '-1',
'deleted': False,
'lastModifiedTimestamp': '0',
'playlistId': playlist_id,
'source': 1,
'trackId': song_id,
}
if song_id.startswith('T'):
m_details['source'] = 2 # AA track
if i > 0:
m_details['precedingEntryId'] = prev_id
if i < len(song_ids) - 1:
m_details['followingEntryId'] = next_id
mutations.append({'create': m_details})
prev_id, cur_id, next_id = cur_id, next_id, str(uuid1())
return mutations
class GetDeviceManagementInfo(McCall):
"""Get registered device information."""
static_method = 'GET'
static_url = sj_url + "devicemanagementinfo"
static_params = {'alt': 'json'}
_device_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'id': {'type': 'string'},
'friendlyName': {'type': 'string', 'blank': True},
'type': {'type': 'string'},
'lastAccessedTimeMs': {'type': 'integer'},
# only for mobile devices
'smartPhone': {'type': 'boolean', 'required': False},
}
}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'data': {'type': 'object',
'items': {'type': 'array', 'items': _device_schema},
'required': False,
},
},
}
class DeauthDevice(McCall):
"""Deauthorize a device from devicemanagementinfo."""
static_method = 'DELETE'
static_url = sj_url + "devicemanagementinfo"
@staticmethod
def dynamic_params(device_id):
return {'delete-id': device_id}
class ListPromotedTracks(McListCall):
item_schema = sj_track
filter_text = 'tracks'
static_method = 'POST'
static_url = sj_url + 'ephemeral/top'
class ListListenNowItems(McCall):
static_method = 'GET'
static_url = sj_url + "listennow/getlistennowitems"
static_params = {'alt': 'json'}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'listennow_items': {
'type': 'array',
'items': {'type': sj_listen_now_item}
}
}
}
@staticmethod
def filter_response(msg):
filtered = copy.deepcopy(msg)
if 'listennow_items' in filtered:
filtered['listennow_items'] = \
["<%s listennow_items>" % len(filtered['listennow_items'])]
class ListListenNowSituations(McCall):
static_method = 'POST'
static_url = sj_url + 'listennow/situations'
static_headers = {'Content-Type': 'application/json'}
static_params = {'alt': 'json'}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'distilledContextWrapper': {'type': 'object',
'distilledContextToken': {'type': 'string'},
'required': False},
'primaryHeader': {'type': 'string'},
'subHeader': {'type': 'string'},
'situations': {'type': 'array',
'items': sj_situation,
},
},
}
@classmethod
def dynamic_data(cls):
tz_offset = calendar.timegm(time.localtime()) - calendar.timegm(time.gmtime())
return json.dumps({
'requestSignals': {'timeZoneOffsetSecs': tz_offset}
})
@staticmethod
def filter_response(msg):
filtered = copy.deepcopy(msg)
if 'situations' in filtered.get('data', {}):
filtered['data']['situations'] = \
["<%s situations>" % len(filtered['data']['situations'])]
return filtered
class GetBrowsePodcastHierarchy(McCall):
static_method = 'GET'
static_url = sj_url + 'podcast/browsehierarchy'
static_params = {'alt': 'json'}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'groups': {
'type': 'array',
'required': False, # Only on errors
'items': sj_podcast_genre
}
}
}
class ListBrowsePodcastSeries(McCall):
static_method = 'GET'
static_url = sj_url + 'podcast/browse'
static_params = {'alt': 'json'}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'series': {
'type': 'array',
'items': {'type': sj_podcast_series}
}
}
}
@classmethod
def dynamic_params(cls, id=None):
return {'id': id}
@staticmethod
def filter_response(msg):
filtered = copy.deepcopy(msg)
if 'series' in filtered:
filtered['series'] = \
["<%s podcasts>" % len(filtered['series'])]
class BatchMutatePodcastSeries(McBatchMutateCall):
static_method = 'POST'
static_url = sj_url + 'podcastseries/batchmutate'
@staticmethod
def build_podcast_updates(updates):
"""
:param updates: ``{'seriesId': '', 'subscribed': '', 'userPreferences':
{'notifyOnNewEpisode': '', 'subscribed': ''}}...``
"""
return [{'update': update} for update in updates]
# The podcastseries and podcastepisode list calls are strange in that they require a device
# ID and pass updated-min, max-results, and start-token as params.
# The start-token param is required, even if not given, to get a result for more than one
# call in a session.
class ListPodcastSeries(McListCall):
item_schema = sj_podcast_series
filter_text = 'podcast series'
static_method = 'GET'
static_url = sj_url + 'podcastseries'
@staticmethod
def dynamic_headers(device_id, updated_after=None, start_token=None, max_results=None):
return {'X-Device-ID': device_id}
@classmethod
def dynamic_params(cls, device_id=None, updated_after=None, start_token=None, max_results=None):
params = {}
if updated_after is None:
microseconds = -1
else:
microseconds = utils.datetime_to_microseconds(updated_after)
params['updated-min'] = microseconds
params['start-token'] = start_token
if max_results is not None:
params['max-results'] = str(max_results)
return params
@classmethod
def dynamic_data(cls, device_id=None, updated_after=None, start_token=None, max_results=None):
pass
# The podcastseries and podcastepisode list calls are strange in that they require a device
# ID and pass updated-min, max-results, and start-token as params.
# The start-token param is required, even if not given, to get a result for more than one
# call in a session.
class ListPodcastEpisodes(McListCall):
item_schema = sj_podcast_episode
filter_text = 'podcast episodes'
static_method = 'GET'
static_url = sj_url + 'podcastepisode'
@staticmethod
def dynamic_headers(device_id, updated_after=None, start_token=None, max_results=None):
return {'X-Device-ID': device_id}
@classmethod
def dynamic_params(cls, device_id=None, updated_after=None, start_token=None, max_results=None):
params = {}
if updated_after is None:
microseconds = -1
else:
microseconds = utils.datetime_to_microseconds(updated_after)
params['updated-min'] = microseconds
params['start-token'] = start_token
if max_results is not None:
params['max-results'] = str(max_results)
return params
@classmethod
def dynamic_data(cls, device_id=None, updated_after=None, start_token=None, max_results=None):
pass
class GetPodcastEpisodeStreamUrl(McStreamCall):
static_method = 'GET'
static_url = sj_stream_url + 'fplay'
class ListStations(McListCall):
item_schema = sj_station
filter_text = 'stations'
static_method = 'POST'
static_url = sj_url + 'radio/station'
class ListStationTracks(McCall):
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'data': {'type': 'object',
'stations': {'type': 'array', 'items': sj_station},
'required': False,
},
},
}
static_headers = {'Content-Type': 'application/json'}
static_params = {'alt': 'json', 'include-tracks': 'true'}
static_method = 'POST'
static_url = sj_url + 'radio/stationfeed'
@staticmethod
def dynamic_data(station_id, num_entries, recently_played):
"""
:param station_id:
:param num_entries: maximum number of tracks to return
:param recently_played: a list of...song ids? never seen an example
"""
# TODO
# clearly, this supports more than one at a time,
# but then that might introduce paging?
# I'll leave it for someone else
if station_id == 'IFL':
return json.dumps({'contentFilter': 1,
'stations': [
{
'numEntries': num_entries,
'recentlyPlayed': recently_played,
'seed': {'seedType': 6}
}
]})
return json.dumps({'contentFilter': 1,
'stations': [
{
'numEntries': num_entries,
'radioId': station_id,
'recentlyPlayed': recently_played
}
]})
@staticmethod
def filter_response(msg):
filtered = copy.deepcopy(msg)
if 'stations' in filtered.get('data', {}):
filtered['data']['stations'] = \
["<%s stations>" % len(filtered['data']['stations'])]
return filtered
class BatchMutateStations(McBatchMutateCall):
static_method = 'POST'
static_url = sj_url + 'radio/editstation'
@staticmethod
def build_deletes(station_ids):
"""
:param station_ids:
"""
return [{'delete': id, 'includeFeed': False, 'numEntries': 0}
for id in station_ids]
@staticmethod
def build_add(name, seed, include_tracks, num_tracks, recent_datetime=None):
"""
:param name: the title
:param seed: a dict {'itemId': id, 'seedType': int}
:param include_tracks: if True, return `num_tracks` tracks in the response
:param num_tracks:
:param recent_datetime: purpose unknown. defaults to now.
"""
if recent_datetime is None:
recent_datetime = datetime.now()
recent_timestamp = utils.datetime_to_microseconds(recent_datetime)
return {
"createOrGet": {
"clientId": str(uuid1()),
"deleted": False,
"imageType": 1,
"lastModifiedTimestamp": "-1",
"name": name,
"recentTimestamp": str(recent_timestamp),
"seed": seed,
"tracks": []
},
"includeFeed": include_tracks,
"numEntries": num_tracks,
"params":
{
"contentFilter": 1
}
}
class BatchMutateTracks(McBatchMutateCall):
static_method = 'POST'
static_url = sj_url + 'trackbatch'
# utility functions to build the mutation dicts
@staticmethod
def build_track_deletes(track_ids):
"""
:param track_ids:
"""
return [{'delete': id} for id in track_ids]
@staticmethod
def build_track_add(store_track_info):
"""
:param store_track_info: sj_track
"""
track_dict = copy.deepcopy(store_track_info)
for key in ('kind', 'trackAvailableForPurchase',
'albumAvailableForPurchase', 'albumArtRef',
'artistId',
):
if key in track_dict:
del track_dict[key]
for key, default in {
'playCount': 0,
'rating': '0',
'genre': '',
'lastModifiedTimestamp': '0',
'deleted': False,
'beatsPerMinute': -1,
'composer': '',
'creationTimestamp': '-1',
'totalDiscCount': 0,
}.items():
track_dict.setdefault(key, default)
# TODO unsure about this
track_dict['trackType'] = 8
return {'create': track_dict}
class GetPodcastSeries(McCall):
static_method = 'GET'
static_url = sj_url + 'podcast/fetchseries'
static_headers = {'Content-Type': 'application/json'}
static_params = {'alt': 'json'}
_res_schema = sj_podcast_series
@staticmethod
def dynamic_params(podcast_series_id, num_episodes):
return {
'nid': podcast_series_id,
'num': num_episodes}
class GetPodcastEpisode(McCall):
static_method = 'GET'
static_url = sj_url + 'podcast/fetchepisode'
static_headers = {'Content-Type': 'application/json'}
static_params = {'alt': 'json'}
_res_schema = sj_podcast_episode
@staticmethod
def dynamic_params(podcast_episode_id):
return {'nid': podcast_episode_id}
class GetStoreTrack(McCall):
# TODO does this accept library ids, too?
static_method = 'GET'
static_url = sj_url + 'fetchtrack'
static_headers = {'Content-Type': 'application/json'}
static_params = {'alt': 'json'}
_res_schema = sj_track
@staticmethod
def dynamic_params(track_id):
return {'nid': track_id}
class GetGenres(McCall):
static_method = 'GET'
static_url = sj_url + 'explore/genres'
static_params = {'alt': 'json'}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'kind': {'type': 'string'},
'genres': {
'type': 'array',
'items': sj_genre,
'required': False, # only on errors
}
}
}
@staticmethod
def dynamic_params(parent_genre_id):
return {'parent-genre': parent_genre_id}
class GetArtist(McCall):
static_method = 'GET'
static_url = sj_url + 'fetchartist'
static_params = {'alt': 'json'}
_res_schema = sj_artist
@staticmethod
def dynamic_params(artist_id, include_albums, num_top_tracks, num_rel_artist):
"""
:param include_albums: bool
:param num_top_tracks: int
:param num_rel_artist: int
"""
return {'nid': artist_id,
'include-albums': include_albums,
'num-top-tracks': num_top_tracks,
'num-related-artists': num_rel_artist,
}
class GetAlbum(McCall):
static_method = 'GET'
static_url = sj_url + 'fetchalbum'
static_params = {'alt': 'json'}
_res_schema = sj_album
@staticmethod
def dynamic_params(album_id, tracks):
include_tracks = bool(tracks) if tracks else None
return {'nid': album_id, 'include-tracks': include_tracks}
class IncrementPlayCount(McCall):
static_method = 'POST'
static_url = sj_url + 'trackstats'
static_params = {'alt': 'json'}
static_headers = {'Content-Type': 'application/json'}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'responses': {
'type': 'array',
'items': {
'type': 'object',
'additionalProperties': False,
'properties': {
'id': {'type': 'string',
'required': False}, # not provided for AA tracks?
'response_code': {'type': 'string'},
}
}
}
}
}
@staticmethod
def dynamic_data(sid, plays, playtime):
# TODO this can support multiple tracks at a time
play_timestamp = utils.datetime_to_microseconds(playtime)
event = {
'context_type': 1,
'event_timestamp_micros': str(play_timestamp),
'event_type': 2,
# This can also send a context_id which is the album/artist id
# the song was found from.
}
return json.dumps({'track_stats': [{
'id': sid,
'incremental_plays': plays,
'last_play_time_millis': str(play_timestamp // 1000),
'type': 2 if sid.startswith('T') else 1,
'track_events': [event] * plays,
}]})
gmusicapi-12.1.1/gmusicapi/protocol/musicmanager.py 0000664 0001750 0001750 00000055133 13400371522 022726 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
"""Calls made by the Music Manager (related to uploading)."""
from __future__ import print_function, division, absolute_import, unicode_literals
from six import raise_from
from builtins import * # noqa
import base64
import hashlib
import itertools
import os
import shutil
from tempfile import NamedTemporaryFile
import dateutil.parser
from decorator import decorator
from google.protobuf.message import DecodeError
import mutagen
import json
from gmusicapi.exceptions import CallFailure
from gmusicapi.protocol import upload_pb2, locker_pb2, download_pb2
from gmusicapi.protocol.shared import Call, ParseException, authtypes
from gmusicapi.utils import utils
log = utils.DynamicClientLogger(__name__)
_android_url = 'https://android.clients.google.com/upsj/'
@decorator
def pb(f, *args, **kwargs):
"""Decorator to serialize a protobuf message."""
msg = f(*args, **kwargs)
return msg.SerializeToString()
class MmCall(Call):
"""Abstract base for Music Manager calls."""
static_method = 'POST'
# remember that setting this in a subclass overrides, not merges
# static + dynamic does merge, though
static_headers = {'User-agent': 'Music Manager (1, 0, 55, 7425 HTTPS - Windows)'}
required_auth = authtypes(oauth=True)
# this is a shared union class that has all specific upload types
# nearly all of the proto calls return a message of this form
res_msg_type = upload_pb2.UploadResponse
@classmethod
def parse_response(cls, response):
"""Parse the cls.res_msg_type proto msg."""
res_msg = cls.res_msg_type()
try:
res_msg.ParseFromString(response.content)
except DecodeError as e:
raise_from(ParseException(str(e)), e)
return res_msg
@classmethod
def filter_response(cls, msg):
return Call._filter_proto(msg)
class GetClientState(MmCall):
static_url = _android_url + 'clientstate'
@classmethod
@pb
def dynamic_data(cls, uploader_id):
"""
:param uploader_id: MM uses host MAC address
"""
req_msg = upload_pb2.ClientStateRequest()
req_msg.uploader_id = uploader_id
return req_msg
class AuthenticateUploader(MmCall):
"""Sent to auth, reauth, or register our upload client."""
static_url = _android_url + 'upauth'
@classmethod
def check_success(cls, response, msg):
if msg.HasField('auth_status') and msg.auth_status != upload_pb2.UploadResponse.OK:
enum_desc = upload_pb2._UPLOADRESPONSE.enum_types[1]
res_name = enum_desc.values_by_number[msg.auth_status].name
raise CallFailure(
"Upload auth error code %s: %s."
" See http://goo.gl/O6xe7 for more information. " % (
msg.auth_status, res_name
), cls.__name__
)
@classmethod
@pb
def dynamic_data(cls, uploader_id, uploader_friendly_name):
"""
:param uploader_id: MM uses host MAC address
:param uploader_friendly_name: MM uses hostname
"""
req_msg = upload_pb2.UpAuthRequest()
req_msg.uploader_id = uploader_id
req_msg.friendly_name = uploader_friendly_name
return req_msg
class UploadMetadata(MmCall):
static_url = _android_url + 'metadata'
static_params = {'version': 1}
@staticmethod
def get_track_clientid(filepath):
# The id is a 22 char hash of the file. It is found by:
# stripping tags
# getting an md5 sum
# converting sum to base64
# removing trailing ===
m = hashlib.md5()
try:
ext = os.path.splitext(filepath)[1]
# delete=False is needed because the NamedTemporaryFile
# can't be opened by name a second time on Windows otherwise.
with NamedTemporaryFile(suffix=ext, delete=False) as temp:
shutil.copy(filepath, temp.name)
audio = mutagen.File(temp.name, easy=True)
audio.delete()
audio.save()
while True:
data = temp.read(65536)
if not data:
break
m.update(data)
finally:
try:
os.remove(temp.name)
except OSError:
log.exception("Could not remove temporary file %r", temp.name)
return base64.encodestring(m.digest())[:-3]
# these collections define how locker_pb2.Track fields align to mutagen's.
shared_fields = ('album', 'artist', 'composer', 'genre')
field_map = { # mutagen: Track
'albumartist': 'album_artist',
'bpm': 'beats_per_minute',
}
count_fields = { # mutagen: (part, total)
'discnumber': ('disc_number', 'total_disc_count'),
'tracknumber': ('track_number', 'total_track_count'),
}
@classmethod
def fill_track_info(cls, filepath):
"""Given the path and contents of a track, return a filled locker_pb2.Track.
On problems, raise ValueError."""
track = locker_pb2.Track()
# The track protobuf message supports an additional metadata list field.
# ALBUM_ART_HASH has been observed being sent in this field so far.
# Append locker_pb2.AdditionalMetadata objects to additional_metadata.
# AdditionalMetadata objects consist of two fields, 'tag_name' and 'value'.
additional_metadata = []
track.client_id = cls.get_track_clientid(filepath)
audio = mutagen.File(filepath, easy=True)
if audio is None:
raise ValueError("could not open to read metadata")
elif isinstance(audio, mutagen.asf.ASF):
# WMA entries store more info than just the value.
# Monkeypatch in a dict {key: value} to keep interface the same for all filetypes.
asf_dict = dict((k, [ve.value for ve in v]) for (k, v) in audio.tags.as_dict().items())
audio.tags = asf_dict
extension = os.path.splitext(filepath)[1].upper()
if isinstance(extension, bytes):
extension = extension.decode('utf8')
if extension:
# Trim leading period if it exists (ie extension not empty).
extension = extension[1:]
if isinstance(audio, mutagen.mp4.MP4) and (
audio.info.codec == 'alac' or audio.info.codec_description == 'ALAC'):
extension = 'ALAC'
elif isinstance(audio, mutagen.mp4.MP4) and audio.info.codec_description.startswith('AAC'):
extension = 'AAC'
if extension.upper() == 'M4B':
# M4B are supported by the music manager, and transcoded like normal.
extension = 'M4A'
if not hasattr(locker_pb2.Track, extension):
raise ValueError("unsupported filetype: {0} for file {1}".format(extension, filepath))
track.original_content_type = getattr(locker_pb2.Track, extension)
track.estimated_size = os.path.getsize(filepath)
track.last_modified_timestamp = int(os.path.getmtime(filepath))
# These are typically zeroed in my examples.
track.play_count = 0
track.client_date_added = 0
track.recent_timestamp = 0
track.rating = locker_pb2.Track.NOT_RATED # star rating
track.duration_millis = int(audio.info.length * 1000)
try:
bitrate = audio.info.bitrate // 1000
except AttributeError:
# mutagen doesn't provide bitrate for some lossless formats (eg FLAC), so
# provide an estimation instead. This shouldn't matter too much;
# the bitrate will always be > 320, which is the highest scan and match quality.
bitrate = (track.estimated_size * 8) // track.duration_millis
track.original_bit_rate = bitrate
# Populate metadata.
def track_set(field_name, val, msg=track):
"""Returns result of utils.pb_set and logs on failures.
Should be used when setting directly from metadata."""
success = utils.pb_set(msg, field_name, val)
if not success:
log.info("could not pb_set track.%s = %r for '%r'", field_name, val, filepath)
return success
# Title is required.
# If it's not in the metadata, the filename will be used.
if "title" in audio:
title = audio['title'][0]
if isinstance(title, mutagen.asf.ASFUnicodeAttribute):
title = title.value
track_set('title', title)
else:
# Assume ascii or unicode.
track.title = os.path.basename(filepath)
if "date" in audio:
date_val = str(audio['date'][0])
try:
datetime = dateutil.parser.parse(date_val, fuzzy=True)
except (ValueError, TypeError) as e:
# TypeError provides compatibility with:
# https://bugs.launchpad.net/dateutil/+bug/1247643
log.warning("could not parse date md for '%r': (%s)", filepath, e)
else:
track_set('year', datetime.year)
for null_field in ['artist', 'album']:
# If these fields aren't provided, they'll render as "undefined" in the web interface;
# see https://github.com/simon-weber/gmusicapi/issues/236.
# Defaulting them to an empty string fixes this.
if null_field not in audio:
track_set(null_field, '')
# Mass-populate the rest of the simple fields.
# Merge shared and unshared fields into {mutagen: Track}.
fields = dict(
itertools.chain(
((shared, shared) for shared in cls.shared_fields),
cls.field_map.items()))
for mutagen_f, track_f in fields.items():
if mutagen_f in audio:
track_set(track_f, audio[mutagen_f][0])
for mutagen_f, (track_f, track_total_f) in cls.count_fields.items():
if mutagen_f in audio:
numstrs = str(audio[mutagen_f][0]).split("/")
track_set(track_f, numstrs[0])
if len(numstrs) == 2 and numstrs[1]:
track_set(track_total_f, numstrs[1])
if additional_metadata:
track.track_extras.additional_metadata.extend(additional_metadata)
return track
@classmethod
@pb
def dynamic_data(cls, tracks, uploader_id, do_not_rematch=False):
"""
:param tracks: list of filled locker_pb2.Track
:param uploader_id:
:param do_not_rematch: seems to be ignored
"""
req_msg = upload_pb2.UploadMetadataRequest()
req_msg.track.extend(tracks)
for track in req_msg.track:
track.do_not_rematch = do_not_rematch
req_msg.uploader_id = uploader_id
return req_msg
class GetUploadJobs(MmCall):
# TODO
static_url = _android_url + 'getjobs'
static_params = {'version': 1}
@classmethod
def check_success(cls, response, msg):
if msg.HasField('getjobs_response') and not msg.getjobs_response.get_tracks_success:
raise CallFailure('get_tracks_success == False', cls.__name__)
@classmethod
@pb
def dynamic_data(cls, uploader_id):
"""
:param uploader_id: MM uses host MAC address
"""
req_msg = upload_pb2.GetJobsRequest()
req_msg.uploader_id = uploader_id
return req_msg
class GetUploadSession(MmCall):
"""Called when we want to upload; the server returns the url to use.
This is a json call, and doesn't share much with the other calls."""
static_method = 'POST'
static_url = 'https://uploadsj.clients.google.com/uploadsj/scottyagent'
@classmethod
def parse_response(cls, response):
return cls._parse_json(response.text)
@staticmethod
def filter_response(res):
return res
@staticmethod
def dynamic_data(uploader_id, num_already_uploaded,
track, filepath, server_id, do_not_rematch=False):
"""track is a locker_pb2.Track, and the server_id is from a metadata upload."""
# small info goes inline, big things get their own external PUT.
# still not sure as to thresholds - I've seen big album art go inline.
if isinstance(filepath, bytes):
filepath = filepath.decode('utf8')
inlined = {
"title": "jumper-uploader-title-42",
"ClientId": track.client_id,
"ClientTotalSongCount": "1", # TODO think this is ie "how many will you upload"
"CurrentTotalUploadedCount": str(num_already_uploaded),
"CurrentUploadingTrack": track.title,
"ServerId": server_id,
"SyncNow": "true",
"TrackBitRate": track.original_bit_rate,
"TrackDoNotRematch": str(do_not_rematch).lower(),
"UploaderId": uploader_id,
}
message = {
"clientId": "Jumper Uploader",
"createSessionRequest": {
"fields": [
{
"external": {
"filename": os.path.basename(filepath),
"name": os.path.abspath(filepath),
"put": {},
# used to use this; don't see it in examples
# "size": track.estimated_size,
}
}
]
},
"protocolVersion": "0.8"
}
# Insert the inline info.
for key in inlined:
payload = inlined[key]
if not isinstance(payload, str):
payload = str(payload)
message['createSessionRequest']['fields'].append(
{
"inlined": {
"content": payload,
"name": key
}
}
)
return json.dumps(message)
@staticmethod
def process_session(res):
"""Return (got_session, error_details).
error_details is (should_retry, reason, error_code) or None if got_session."""
if 'sessionStatus' in res:
return (True, None)
if 'errorMessage' in res:
try:
# This terribly nested structure is Google's doing.
error_code = (res['errorMessage']['additionalInfo']
['uploader_service.GoogleRupioAdditionalInfo']['completionInfo']
['customerSpecificInfo']['ResponseCode'])
except KeyError:
# The returned nested structure is not as expected: cannot get Response Code
error_code = None
got_session = False
if error_code == 503:
should_retry = True
reason = 'upload servers still syncing'
# TODO unsure about these codes
elif error_code == 200:
should_retry = False
reason = 'this song is already uploaded'
elif error_code == 404:
should_retry = False
reason = 'the request was rejected'
else:
should_retry = True
reason = 'the server reported an unknown error'
return (got_session, (should_retry, reason, error_code))
return (False, (True, "the server's response could not be understood", None))
class UploadFile(MmCall):
"""Called after getting a session to actually upload a file."""
# TODO recent protocols use multipart encoding
static_method = 'PUT'
@classmethod
def parse_response(cls, response):
return cls._parse_json(response.text)
@staticmethod
def filter_response(res):
return res
@staticmethod
def dynamic_headers(session_url, content_type, audio):
return {'CONTENT-TYPE': content_type}
@staticmethod
def dynamic_url(session_url, content_type, audio):
# this actually includes params, but easier to pass them straight through
return session_url
@staticmethod
def dynamic_data(session_url, content_type, audio):
return audio
class ProvideSample(MmCall):
"""Give the server a scan and match sample.
The sample is a 128k mp3 slice of the file, usually 15 seconds long."""
static_method = 'POST'
static_params = {'version': 1}
static_url = _android_url + 'sample'
@staticmethod
@pb
def dynamic_data(filepath, server_challenge, track, uploader_id, mock_sample=None):
"""Raise IOError on transcoding problems, or ValueError for invalid input.
:param mock_sample: if provided, will be sent in place of a proper sample
"""
msg = upload_pb2.UploadSampleRequest()
msg.uploader_id = uploader_id
sample_msg = upload_pb2.TrackSample()
sample_msg.track.CopyFrom(track)
sample_msg.signed_challenge_info.CopyFrom(server_challenge)
sample_spec = server_challenge.challenge_info # convenience
if mock_sample is None:
# The sample is simply a small (usually 15 second) clip of the song,
# transcoded into 128kbs mp3. The server dictates where the cut should be made.
sample_msg.sample = utils.transcode_to_mp3(
filepath, quality='128k',
slice_start=sample_spec.start_millis // 1000,
slice_duration=sample_spec.duration_millis // 1000
)
else:
sample_msg.sample = mock_sample
# You can provide multiple samples; I just provide one at a time.
msg.track_sample.extend([sample_msg])
return msg
class UpdateUploadState(MmCall):
"""Notify the server that we will be starting/stopping/pausing our upload.
I believe this is used for the webclient 'currently uploading' widget, but that might also be
the current_uploading information.
"""
static_method = 'POST'
static_params = {'version': 1}
static_url = _android_url + 'uploadstate'
@staticmethod
@pb
def dynamic_data(to_state, uploader_id):
"""Raise ValueError on problems.
:param to_state: one of 'start', 'paused', or 'stopped'
"""
msg = upload_pb2.UpdateUploadStateRequest()
msg.uploader_id = uploader_id
try:
state = getattr(upload_pb2.UpdateUploadStateRequest, to_state.upper())
except AttributeError as e:
raise ValueError(str(e))
msg.state = state
return msg
class CancelUploadJobs(MmCall):
"""This call will cancel any outstanding upload jobs (ie from GetJobs).
The Music Manager only calls it when the user changes the location of their local collection.
It doesn't actually return anything useful."""
static_method = 'POST'
static_url = _android_url + 'deleteuploadrequested'
@staticmethod
@pb
def dynamic_data(uploader_id):
"""
:param uploader_id: id
"""
msg = upload_pb2.DeleteUploadRequestedRequest() # what a mouthful!
msg.uploader_id = uploader_id
return msg
class ListTracks(MmCall):
"""List all tracks. Returns a subset of all available metadata.
Can optionally filter for only free/purchased tracks."""
res_msg_type = download_pb2.GetTracksToExportResponse
static_method = 'POST'
static_url = 'https://music.google.com/music/exportids'
# example response:
# download_track_info {
# id: "970d9e51-b392-3857-897a-170e456cba60"
# title: "Temporary Trip"
# album: "Pay Attention"
# album_artist: "The Mighty Mighty Bosstones"
# artist: "The Mighty Mighty Bosstones"
# track_number: 14
# track_size: 3577382
# }
@staticmethod
def dynamic_headers(client_id, *args, **kwargs):
return {'X-Device-ID': client_id}
@staticmethod
@pb
def dynamic_data(client_id, cont_token=None, export_type=1, updated_min=0):
"""Works similarly to the webclient method.
Chunks are up to 1000 tracks.
:param client_id: an authorized uploader_id
:param cont_token: (optional) token to get the next library chunk.
:param export_type: 1='ALL', 2='PURCHASED_AND_PROMOTIONAL'
:param updated_min: likely a timestamp; never seen an example of this != 0
"""
msg = download_pb2.GetTracksToExportRequest()
msg.client_id = client_id
msg.export_type = export_type
if cont_token is not None:
msg.continuation_token = cont_token
msg.updated_min = updated_min
return msg
@classmethod
def check_success(cls, response, msg):
if msg.status != download_pb2.GetTracksToExportResponse.OK:
enum_desc = download_pb2._GETTRACKSTOEXPORTRESPONSE.enum_types[0]
res_name = enum_desc.values_by_number[msg.status].name
raise CallFailure(
"Track export (list) error code %s: %s." % (
msg.status, res_name
), cls.__name__
)
# TODO
@staticmethod
def filter_response(msg):
"""Only log a summary."""
cont_token = None
if msg.HasField('continuation_token'):
cont_token = msg.continuation_token
updated_min = None
if msg.HasField('updated_min'):
updated_min = msg.updated_min
return "<%s songs>, updated_min: %r, continuation_token: %r" % (
len(msg.download_track_info),
updated_min,
cont_token)
class GetDownloadLink(MmCall):
"""Get a url where a track can be downloaded.
Auth is not needed to retrieve the resulting url."""
static_method = 'GET'
static_headers = {}
static_params = {'version': 2}
static_url = 'https://music.google.com/music/export'
@staticmethod
def dynamic_headers(sid, client_id):
return {'X-Device-ID': client_id}
@staticmethod
def dynamic_params(sid, client_id):
return {'songid': sid}
@classmethod
def parse_response(cls, response):
return cls._parse_json(response.text)
@staticmethod
def filter_response(res):
return res
class DownloadTrack(MmCall):
"""Given a url, retrieve a track. Unlike the Webclient, this
requires authentication.
The entire Requests.Response is returned."""
static_method = 'GET'
@staticmethod
def dynamic_url(url):
"""
:param url: result of a call to GetDownloadLink
"""
return url
@classmethod
def parse_response(cls, response):
return response
@staticmethod
def filter_response(res):
return "code: %s; size: %s bytes; disposition: %r" % (
res.status_code,
res.headers['Content-Length'],
res.headers['Content-Disposition'])
gmusicapi-12.1.1/gmusicapi/protocol/shared.py 0000664 0001750 0001750 00000027734 13400371522 021527 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
"""Definitions shared by multiple clients."""
from __future__ import print_function, division, absolute_import, unicode_literals
from builtins import * # noqa
from six import raise_from
from collections import namedtuple
from google.protobuf.descriptor import FieldDescriptor
import json
from gmusicapi.exceptions import (
CallFailure, ParseException, ValidationException,
)
from gmusicapi.utils import utils
import requests
from future.utils import with_metaclass
log = utils.DynamicClientLogger(__name__)
_auth_names = ('xt', 'sso', 'oauth', 'gpsoauth',)
"""
AuthTypes has fields for each type of auth, each of which store a bool:
xt: webclient xsrf param/cookie
sso: webclient Authorization header
oauth: musicmanager/mobileclient Bearer header
gpsoauth: mobileclient gpsoauth GoogleLogin header
"""
AuthTypes = namedtuple('AuthTypes', _auth_names)
def authtypes(**kwargs):
"""Convinience factory for AuthTypes that defaults authtypes to False."""
for name in _auth_names:
if name not in kwargs:
kwargs[name] = False
return AuthTypes(**kwargs)
class BuildRequestMeta(type):
"""Metaclass to create build_request from static/dynamic config."""
def __new__(cls, name, bases, dct):
# To not mess with mro and inheritance, build the class first.
new_cls = super(BuildRequestMeta, cls).__new__(cls, name, bases, dct)
merge_keys = ('headers', 'params')
all_keys = ('method', 'url', 'files', 'data', 'verify', 'allow_redirects') + merge_keys
config = {} # stores key: val for static or f(*args, **kwargs) -> val for dyn
dyn = lambda key: 'dynamic_' + key # noqa
stat = lambda key: 'static_' + key # noqa
has_key = lambda key: hasattr(new_cls, key) # noqa
get_key = lambda key: getattr(new_cls, key) # noqa
for key in all_keys:
if not has_key(dyn(key)) and not has_key(stat(key)):
continue # this key will be ignored; requests will default it
if has_key(dyn(key)):
config[key] = get_key(dyn(key))
else:
config[key] = get_key(stat(key))
for key in merge_keys:
# merge case: dyn took precedence above, but stat also exists
if has_key(dyn(key)) and has_key(stat(key)):
def key_closure(stat_val=get_key(stat(key)), dyn_func=get_key(dyn(key))):
def build_key(*args, **kwargs):
dyn_val = dyn_func(*args, **kwargs)
stat_val.update(dyn_val)
return stat_val
return build_key
config[key] = key_closure()
# To explain some of the funkiness wrt closures, see:
# http://stackoverflow.com/questions/233673/lexical-closures-in-python
# create the actual build_request method
def req_closure(config=config):
def build_request(cls, *args, **kwargs):
req_kwargs = {}
for key, val in config.items():
if hasattr(val, '__call__'):
val = val(*args, **kwargs)
req_kwargs[key] = val
return req_kwargs
return build_request
new_cls.build_request = classmethod(req_closure())
return new_cls
class Call(with_metaclass(BuildRequestMeta, object)):
"""
Clients should use Call.perform().
Calls define how to build their requests through static and dynamic data.
For example, a request might always send some user-agent: this is static.
Or, it might need the name of a song to modify: this is dynamic.
Specially named fields define the data, and correspond with requests.Request kwargs:
method: eg 'GET' or 'POST'
url: string
files: dictionary of {filename: fileobject} files to multipart upload.
data: the body of the request
If a dictionary is provided, form-encoding will take place.
A string will be sent as-is.
verify: if True, verify SSL certs
params (m): dictionary of URL parameters to append to the URL.
headers (m): dictionary
Static data shold prepends static_ to a field:
class SomeCall(Call):
static_url = 'http://foo.com/thiscall'
And dynamic data prepends dynamic_ to a method:
class SomeCall(Call):
#*args, **kwargs are passed from SomeCall.build_request (and Call.perform)
def dynamic_url(endpoint):
return 'http://foo.com/' + endpoint
Dynamic data takes precedence over static if both exist,
except for attributes marked with (m) above. These get merged, with dynamic overriding
on key conflicts (though all this really shouldn't be relied on).
Here's a contrived example that merges static and dynamic headers:
class SomeCall(Call):
static_headers = {'user-agent': "I'm totally a Google client!"}
@classmethod
def dynamic_headers(cls, keep_alive=False):
return {'Connection': keep_alive}
If neither a static nor dynamic member is defined,
the param is not used to create the requests.Request.
Calls declare the kind of auth they require with an AuthTypes object named required_auth.
Calls must define parse_response.
Calls can also define filter_response, validate and check_success.
Calls are organized semantically, so one endpoint might have multiple calls.
"""
gets_logged = True
fail_on_non_200 = True
required_auth = authtypes() # all false by default
@classmethod
def parse_response(cls, response):
"""Parses a requests.Response to data."""
raise NotImplementedError
@classmethod
def validate(cls, response, msg):
"""Raise ValidationException on problems.
:param response: a requests.Response
:param msg: the result of parse_response on response
"""
pass
@classmethod
def check_success(cls, response, msg):
"""Raise CallFailure on problems.
:param response: a requests.Response
:param msg: the result of parse_response on response
"""
pass
@classmethod
def filter_response(cls, msg):
"""Return a version of a parsed response appropriate for logging."""
return msg # default to identity
@classmethod
def perform(cls, session, validate, *args, **kwargs):
"""Send, parse, validate and check success of this call.
*args and **kwargs are passed to protocol.build_transaction.
:param session: a PlaySession used to send this request.
:param validate: if False, do not validate.
:param required_auth: if in kwargs, overrides the static protocol required_auth.
"""
# TODO link up these docs
call_name = cls.__name__
if cls.gets_logged:
log.debug("%s(args=%s, kwargs=%s)",
call_name,
[utils.truncate(a) for a in args],
dict((k, utils.truncate(v)) for (k, v) in kwargs.items())
)
else:
log.debug("%s()", call_name)
required_auth = kwargs.pop('required_auth', cls.required_auth)
req_kwargs = cls.build_request(*args, **kwargs)
response = session.send(req_kwargs, required_auth)
# TODO trim the logged response if it's huge?
safe_req_kwargs = req_kwargs.copy()
if safe_req_kwargs.get('headers', {}).get('Authorization', None) is not None:
safe_req_kwargs['headers']['Authorization'] = ''
if cls.fail_on_non_200:
try:
response.raise_for_status()
except requests.HTTPError as e:
err_msg = str(e)
if cls.gets_logged:
err_msg += "\n(requests kwargs: %r)" % (safe_req_kwargs)
err_msg += "\n(response was: %r)" % response.text
raise CallFailure(err_msg, call_name)
try:
parsed_response = cls.parse_response(response)
except ParseException:
err_msg = ("the server's response could not be understood."
" The call may still have succeeded, but it's unlikely.")
if cls.gets_logged:
err_msg += "\n(requests kwargs: %r)" % (safe_req_kwargs)
err_msg += "\n(response was: %r)" % response.text
log.exception("could not parse %s response: %r", call_name, response.text)
else:
log.exception("could not parse %s response: (omitted)", call_name)
raise CallFailure(err_msg, call_name)
if cls.gets_logged:
log.debug(cls.filter_response(parsed_response))
try:
# order is important; validate only has a schema for a successful response
cls.check_success(response, parsed_response)
if validate:
cls.validate(response, parsed_response)
except CallFailure as e:
if not cls.gets_logged:
raise
# otherwise, reraise a new exception with our req/res context
err_msg = ("{e_message}\n"
"(requests kwargs: {req_kwargs!r})\n"
"(response was: {content!r})").format(
e_message=str(e),
req_kwargs=safe_req_kwargs,
content=response.text)
raise_from(CallFailure(err_msg, e.callname), e)
except ValidationException as e:
# TODO shouldn't be using formatting
err_msg = "the response format for %s was not recognized." % call_name
err_msg += "\n\n%s\n" % e
if cls.gets_logged:
raw_response = response.text
if len(raw_response) > 10000:
raw_response = raw_response[:10000] + '...'
err_msg += ("\nFirst, try the develop branch."
" If you can recreate this error with the most recent code"
" please [create an issue](http://goo.gl/qbAW8) that includes"
" the above ValidationException"
" and the following request/response:\n%r\n\n%r\n"
"\nA traceback follows:\n") % (safe_req_kwargs, raw_response)
log.exception(err_msg)
return parsed_response
@staticmethod
def _parse_json(text):
try:
return json.loads(text)
except ValueError as e:
raise_from(ParseException(str(e)), e)
@staticmethod
def _filter_proto(msg, make_copy=True):
"""Filter all byte fields in the message and submessages."""
filtered = msg
if make_copy:
filtered = msg.__class__()
filtered.CopyFrom(msg)
fields = filtered.ListFields()
# eg of filtering a specific field
# if any(fd.name == 'field_name' for fd, val in fields):
# filtered.field_name = ''
# Filter all byte fields.
for field_name, val in ((fd.name, val) for fd, val in fields
if fd.type == FieldDescriptor.TYPE_BYTES):
setattr(filtered, field_name, bytes("<%s bytes>" % len(val), 'utf8'))
# Filter submessages.
for field in (val for fd, val in fields
if fd.type == FieldDescriptor.TYPE_MESSAGE):
# protobuf repeated api is bad for reflection
is_repeated = hasattr(field, '__len__')
if not is_repeated:
Call._filter_proto(field, make_copy=False)
else:
for i in range(len(field)):
# repeatedComposite does not allow setting
old_fields = [f for f in field]
del field[:]
field.extend([Call._filter_proto(f, make_copy=False)
for f in old_fields])
return filtered
gmusicapi-12.1.1/gmusicapi/protocol/uits_pb2.py 0000664 0001750 0001750 00000071553 13374625453 022025 0 ustar simon simon 0000000 0000000 # Generated by the protocol buffer compiler. DO NOT EDIT!
# source: uits.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
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 import descriptor_pb2
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
DESCRIPTOR = _descriptor.FileDescriptor(
name='uits.proto',
package='wireless_android_skyjam',
syntax='proto2',
serialized_pb=_b('\n\nuits.proto\x12\x17wireless_android_skyjam\"\x83\x01\n\tProductId\x12\x35\n\x04type\x18\x01 \x02(\x0e\x32\'.wireless_android_skyjam.ProductId.Type\x12\x18\n\tcompleted\x18\x02 \x01(\x08:\x05\x66\x61lse\x12\n\n\x02id\x18\x03 \x02(\t\"\x19\n\x04Type\x12\x07\n\x03UPC\x10\x01\x12\x08\n\x04GRID\x10\x02\"\\\n\x07\x41ssetId\x12\x33\n\x04type\x18\x01 \x02(\x0e\x32%.wireless_android_skyjam.AssetId.Type\x12\n\n\x02id\x18\x02 \x02(\t\"\x10\n\x04Type\x12\x08\n\x04ISRC\x10\x01\"/\n\rTransactionId\x12\x12\n\x07version\x18\x01 \x02(\t:\x01\x31\x12\n\n\x02id\x18\x02 \x02(\t\"|\n\x07MediaId\x12\x46\n\x0e\x61lgorithm_type\x18\x01 \x02(\x0e\x32..wireless_android_skyjam.MediaId.AlgorithmType\x12\x0c\n\x04hash\x18\x02 \x02(\t\"\x1b\n\rAlgorithmType\x12\n\n\x06SHA256\x10\x01\"\xa3\x01\n\x07UrlInfo\x12\x33\n\x04type\x18\x01 \x02(\x0e\x32%.wireless_android_skyjam.UrlInfo.Type\x12\x0b\n\x03url\x18\x02 \x02(\t\"V\n\x04Type\x12\x08\n\x04WCOM\x10\x01\x12\x08\n\x04WCOP\x10\x02\x12\x08\n\x04WOAF\x10\x03\x12\x08\n\x04WOAR\x10\x04\x12\x08\n\x04WOAS\x10\x05\x12\x08\n\x04WORS\x10\x06\x12\x08\n\x04WPAY\x10\x07\x12\x08\n\x04WPUB\x10\x08\"\xac\x01\n\x0f\x43opyrightStatus\x12;\n\x04type\x18\x01 \x02(\x0e\x32-.wireless_android_skyjam.CopyrightStatus.Type\x12\x11\n\tcopyright\x18\x02 \x01(\t\"I\n\x04Type\x12\x0f\n\x0bUNSPECIFIED\x10\x01\x12\x15\n\x11\x41LLRIGHTSRESERVED\x10\x02\x12\x0e\n\nPRERELEASE\x10\x03\x12\t\n\x05OTHER\x10\x04\"$\n\x05\x45xtra\x12\x0c\n\x04type\x18\x01 \x02(\t\x12\r\n\x05value\x18\x02 \x02(\t\"\xf5\x04\n\x0cUitsMetadata\x12\r\n\x05nonce\x18\x01 \x02(\t\x12\x16\n\x0e\x64istributor_id\x18\x02 \x02(\t\x12\x18\n\x10transaction_date\x18\x03 \x02(\t\x12\x36\n\nproduct_id\x18\x04 \x02(\x0b\x32\".wireless_android_skyjam.ProductId\x12\x32\n\x08\x61sset_id\x18\x05 \x02(\x0b\x32 .wireless_android_skyjam.AssetId\x12>\n\x0etransaction_id\x18\x06 \x02(\x0b\x32&.wireless_android_skyjam.TransactionId\x12\x32\n\x08media_id\x18\x07 \x02(\x0b\x32 .wireless_android_skyjam.MediaId\x12\x32\n\x08url_info\x18\x08 \x01(\x0b\x32 .wireless_android_skyjam.UrlInfo\x12Z\n\x16parental_advisory_type\x18\t \x01(\x0e\x32:.wireless_android_skyjam.UitsMetadata.ParentalAdvisoryType\x12\x42\n\x10\x63opyright_status\x18\n \x01(\x0b\x32(.wireless_android_skyjam.CopyrightStatus\x12-\n\x05\x65xtra\x18\x0b \x03(\x0b\x32\x1e.wireless_android_skyjam.Extra\"A\n\x14ParentalAdvisoryType\x12\x0f\n\x0bUNSPECIFIED\x10\x01\x12\x0c\n\x08\x45XPLICIT\x10\x02\x12\n\n\x06\x45\x44ITED\x10\x03\"\xa5\x02\n\rUitsSignature\x12L\n\x0e\x61lgorithm_type\x18\x01 \x02(\x0e\x32\x34.wireless_android_skyjam.UitsSignature.AlgorithmType\x12Z\n\x15\x63\x61nonicalization_type\x18\x02 \x02(\x0e\x32;.wireless_android_skyjam.UitsSignature.CanonicalizationType\x12\x0e\n\x06key_id\x18\x03 \x02(\t\x12\r\n\x05value\x18\x04 \x02(\t\")\n\rAlgorithmType\x12\x0b\n\x07RSA2048\x10\x01\x12\x0b\n\x07\x44SA2048\x10\x02\" \n\x14\x43\x61nonicalizationType\x12\x08\n\x04NONE\x10\x01\"z\n\x04Uits\x12\x37\n\x08metadata\x18\x01 \x02(\x0b\x32%.wireless_android_skyjam.UitsMetadata\x12\x39\n\tsignature\x18\x02 \x02(\x0b\x32&.wireless_android_skyjam.UitsSignature')
)
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
_PRODUCTID_TYPE = _descriptor.EnumDescriptor(
name='Type',
full_name='wireless_android_skyjam.ProductId.Type',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='UPC', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='GRID', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=146,
serialized_end=171,
)
_sym_db.RegisterEnumDescriptor(_PRODUCTID_TYPE)
_ASSETID_TYPE = _descriptor.EnumDescriptor(
name='Type',
full_name='wireless_android_skyjam.AssetId.Type',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='ISRC', index=0, number=1,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=249,
serialized_end=265,
)
_sym_db.RegisterEnumDescriptor(_ASSETID_TYPE)
_MEDIAID_ALGORITHMTYPE = _descriptor.EnumDescriptor(
name='AlgorithmType',
full_name='wireless_android_skyjam.MediaId.AlgorithmType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='SHA256', index=0, number=1,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=413,
serialized_end=440,
)
_sym_db.RegisterEnumDescriptor(_MEDIAID_ALGORITHMTYPE)
_URLINFO_TYPE = _descriptor.EnumDescriptor(
name='Type',
full_name='wireless_android_skyjam.UrlInfo.Type',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='WCOM', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='WCOP', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='WOAF', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='WOAR', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='WOAS', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='WORS', index=5, number=6,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='WPAY', index=6, number=7,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='WPUB', index=7, number=8,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=520,
serialized_end=606,
)
_sym_db.RegisterEnumDescriptor(_URLINFO_TYPE)
_COPYRIGHTSTATUS_TYPE = _descriptor.EnumDescriptor(
name='Type',
full_name='wireless_android_skyjam.CopyrightStatus.Type',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='UNSPECIFIED', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ALLRIGHTSRESERVED', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PRERELEASE', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='OTHER', index=3, number=4,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=708,
serialized_end=781,
)
_sym_db.RegisterEnumDescriptor(_COPYRIGHTSTATUS_TYPE)
_UITSMETADATA_PARENTALADVISORYTYPE = _descriptor.EnumDescriptor(
name='ParentalAdvisoryType',
full_name='wireless_android_skyjam.UitsMetadata.ParentalAdvisoryType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='UNSPECIFIED', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='EXPLICIT', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='EDITED', index=2, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=1386,
serialized_end=1451,
)
_sym_db.RegisterEnumDescriptor(_UITSMETADATA_PARENTALADVISORYTYPE)
_UITSSIGNATURE_ALGORITHMTYPE = _descriptor.EnumDescriptor(
name='AlgorithmType',
full_name='wireless_android_skyjam.UitsSignature.AlgorithmType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='RSA2048', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='DSA2048', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=1672,
serialized_end=1713,
)
_sym_db.RegisterEnumDescriptor(_UITSSIGNATURE_ALGORITHMTYPE)
_UITSSIGNATURE_CANONICALIZATIONTYPE = _descriptor.EnumDescriptor(
name='CanonicalizationType',
full_name='wireless_android_skyjam.UitsSignature.CanonicalizationType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='NONE', index=0, number=1,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=1715,
serialized_end=1747,
)
_sym_db.RegisterEnumDescriptor(_UITSSIGNATURE_CANONICALIZATIONTYPE)
_PRODUCTID = _descriptor.Descriptor(
name='ProductId',
full_name='wireless_android_skyjam.ProductId',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='type', full_name='wireless_android_skyjam.ProductId.type', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='completed', full_name='wireless_android_skyjam.ProductId.completed', index=1,
number=2, type=8, cpp_type=7, label=1,
has_default_value=True, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.ProductId.id', index=2,
number=3, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_PRODUCTID_TYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=40,
serialized_end=171,
)
_ASSETID = _descriptor.Descriptor(
name='AssetId',
full_name='wireless_android_skyjam.AssetId',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='type', full_name='wireless_android_skyjam.AssetId.type', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.AssetId.id', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_ASSETID_TYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=173,
serialized_end=265,
)
_TRANSACTIONID = _descriptor.Descriptor(
name='TransactionId',
full_name='wireless_android_skyjam.TransactionId',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='version', full_name='wireless_android_skyjam.TransactionId.version', index=0,
number=1, type=9, cpp_type=9, label=2,
has_default_value=True, default_value=_b("1").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='id', full_name='wireless_android_skyjam.TransactionId.id', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=267,
serialized_end=314,
)
_MEDIAID = _descriptor.Descriptor(
name='MediaId',
full_name='wireless_android_skyjam.MediaId',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='algorithm_type', full_name='wireless_android_skyjam.MediaId.algorithm_type', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='hash', full_name='wireless_android_skyjam.MediaId.hash', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_MEDIAID_ALGORITHMTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=316,
serialized_end=440,
)
_URLINFO = _descriptor.Descriptor(
name='UrlInfo',
full_name='wireless_android_skyjam.UrlInfo',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='type', full_name='wireless_android_skyjam.UrlInfo.type', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='url', full_name='wireless_android_skyjam.UrlInfo.url', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_URLINFO_TYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=443,
serialized_end=606,
)
_COPYRIGHTSTATUS = _descriptor.Descriptor(
name='CopyrightStatus',
full_name='wireless_android_skyjam.CopyrightStatus',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='type', full_name='wireless_android_skyjam.CopyrightStatus.type', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='copyright', full_name='wireless_android_skyjam.CopyrightStatus.copyright', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_COPYRIGHTSTATUS_TYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=609,
serialized_end=781,
)
_EXTRA = _descriptor.Descriptor(
name='Extra',
full_name='wireless_android_skyjam.Extra',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='type', full_name='wireless_android_skyjam.Extra.type', 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,
options=None),
_descriptor.FieldDescriptor(
name='value', full_name='wireless_android_skyjam.Extra.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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=783,
serialized_end=819,
)
_UITSMETADATA = _descriptor.Descriptor(
name='UitsMetadata',
full_name='wireless_android_skyjam.UitsMetadata',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='nonce', full_name='wireless_android_skyjam.UitsMetadata.nonce', 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,
options=None),
_descriptor.FieldDescriptor(
name='distributor_id', full_name='wireless_android_skyjam.UitsMetadata.distributor_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='transaction_date', full_name='wireless_android_skyjam.UitsMetadata.transaction_date', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='product_id', full_name='wireless_android_skyjam.UitsMetadata.product_id', index=3,
number=4, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='asset_id', full_name='wireless_android_skyjam.UitsMetadata.asset_id', index=4,
number=5, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='transaction_id', full_name='wireless_android_skyjam.UitsMetadata.transaction_id', index=5,
number=6, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='media_id', full_name='wireless_android_skyjam.UitsMetadata.media_id', index=6,
number=7, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='url_info', full_name='wireless_android_skyjam.UitsMetadata.url_info', index=7,
number=8, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='parental_advisory_type', full_name='wireless_android_skyjam.UitsMetadata.parental_advisory_type', index=8,
number=9, 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,
options=None),
_descriptor.FieldDescriptor(
name='copyright_status', full_name='wireless_android_skyjam.UitsMetadata.copyright_status', index=9,
number=10, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='extra', full_name='wireless_android_skyjam.UitsMetadata.extra', index=10,
number=11, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_UITSMETADATA_PARENTALADVISORYTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=822,
serialized_end=1451,
)
_UITSSIGNATURE = _descriptor.Descriptor(
name='UitsSignature',
full_name='wireless_android_skyjam.UitsSignature',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='algorithm_type', full_name='wireless_android_skyjam.UitsSignature.algorithm_type', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='canonicalization_type', full_name='wireless_android_skyjam.UitsSignature.canonicalization_type', index=1,
number=2, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='key_id', full_name='wireless_android_skyjam.UitsSignature.key_id', index=2,
number=3, 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,
options=None),
_descriptor.FieldDescriptor(
name='value', full_name='wireless_android_skyjam.UitsSignature.value', index=3,
number=4, type=9, cpp_type=9, label=2,
has_default_value=False, default_value=_b("").decode('utf-8'),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_UITSSIGNATURE_ALGORITHMTYPE,
_UITSSIGNATURE_CANONICALIZATIONTYPE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=1454,
serialized_end=1747,
)
_UITS = _descriptor.Descriptor(
name='Uits',
full_name='wireless_android_skyjam.Uits',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='metadata', full_name='wireless_android_skyjam.Uits.metadata', index=0,
number=1, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='signature', full_name='wireless_android_skyjam.Uits.signature', index=1,
number=2, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=1749,
serialized_end=1871,
)
_PRODUCTID.fields_by_name['type'].enum_type = _PRODUCTID_TYPE
_PRODUCTID_TYPE.containing_type = _PRODUCTID
_ASSETID.fields_by_name['type'].enum_type = _ASSETID_TYPE
_ASSETID_TYPE.containing_type = _ASSETID
_MEDIAID.fields_by_name['algorithm_type'].enum_type = _MEDIAID_ALGORITHMTYPE
_MEDIAID_ALGORITHMTYPE.containing_type = _MEDIAID
_URLINFO.fields_by_name['type'].enum_type = _URLINFO_TYPE
_URLINFO_TYPE.containing_type = _URLINFO
_COPYRIGHTSTATUS.fields_by_name['type'].enum_type = _COPYRIGHTSTATUS_TYPE
_COPYRIGHTSTATUS_TYPE.containing_type = _COPYRIGHTSTATUS
_UITSMETADATA.fields_by_name['product_id'].message_type = _PRODUCTID
_UITSMETADATA.fields_by_name['asset_id'].message_type = _ASSETID
_UITSMETADATA.fields_by_name['transaction_id'].message_type = _TRANSACTIONID
_UITSMETADATA.fields_by_name['media_id'].message_type = _MEDIAID
_UITSMETADATA.fields_by_name['url_info'].message_type = _URLINFO
_UITSMETADATA.fields_by_name['parental_advisory_type'].enum_type = _UITSMETADATA_PARENTALADVISORYTYPE
_UITSMETADATA.fields_by_name['copyright_status'].message_type = _COPYRIGHTSTATUS
_UITSMETADATA.fields_by_name['extra'].message_type = _EXTRA
_UITSMETADATA_PARENTALADVISORYTYPE.containing_type = _UITSMETADATA
_UITSSIGNATURE.fields_by_name['algorithm_type'].enum_type = _UITSSIGNATURE_ALGORITHMTYPE
_UITSSIGNATURE.fields_by_name['canonicalization_type'].enum_type = _UITSSIGNATURE_CANONICALIZATIONTYPE
_UITSSIGNATURE_ALGORITHMTYPE.containing_type = _UITSSIGNATURE
_UITSSIGNATURE_CANONICALIZATIONTYPE.containing_type = _UITSSIGNATURE
_UITS.fields_by_name['metadata'].message_type = _UITSMETADATA
_UITS.fields_by_name['signature'].message_type = _UITSSIGNATURE
DESCRIPTOR.message_types_by_name['ProductId'] = _PRODUCTID
DESCRIPTOR.message_types_by_name['AssetId'] = _ASSETID
DESCRIPTOR.message_types_by_name['TransactionId'] = _TRANSACTIONID
DESCRIPTOR.message_types_by_name['MediaId'] = _MEDIAID
DESCRIPTOR.message_types_by_name['UrlInfo'] = _URLINFO
DESCRIPTOR.message_types_by_name['CopyrightStatus'] = _COPYRIGHTSTATUS
DESCRIPTOR.message_types_by_name['Extra'] = _EXTRA
DESCRIPTOR.message_types_by_name['UitsMetadata'] = _UITSMETADATA
DESCRIPTOR.message_types_by_name['UitsSignature'] = _UITSSIGNATURE
DESCRIPTOR.message_types_by_name['Uits'] = _UITS
ProductId = _reflection.GeneratedProtocolMessageType('ProductId', (_message.Message,), dict(
DESCRIPTOR = _PRODUCTID,
__module__ = 'uits_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.ProductId)
))
_sym_db.RegisterMessage(ProductId)
AssetId = _reflection.GeneratedProtocolMessageType('AssetId', (_message.Message,), dict(
DESCRIPTOR = _ASSETID,
__module__ = 'uits_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.AssetId)
))
_sym_db.RegisterMessage(AssetId)
TransactionId = _reflection.GeneratedProtocolMessageType('TransactionId', (_message.Message,), dict(
DESCRIPTOR = _TRANSACTIONID,
__module__ = 'uits_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.TransactionId)
))
_sym_db.RegisterMessage(TransactionId)
MediaId = _reflection.GeneratedProtocolMessageType('MediaId', (_message.Message,), dict(
DESCRIPTOR = _MEDIAID,
__module__ = 'uits_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.MediaId)
))
_sym_db.RegisterMessage(MediaId)
UrlInfo = _reflection.GeneratedProtocolMessageType('UrlInfo', (_message.Message,), dict(
DESCRIPTOR = _URLINFO,
__module__ = 'uits_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UrlInfo)
))
_sym_db.RegisterMessage(UrlInfo)
CopyrightStatus = _reflection.GeneratedProtocolMessageType('CopyrightStatus', (_message.Message,), dict(
DESCRIPTOR = _COPYRIGHTSTATUS,
__module__ = 'uits_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.CopyrightStatus)
))
_sym_db.RegisterMessage(CopyrightStatus)
Extra = _reflection.GeneratedProtocolMessageType('Extra', (_message.Message,), dict(
DESCRIPTOR = _EXTRA,
__module__ = 'uits_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.Extra)
))
_sym_db.RegisterMessage(Extra)
UitsMetadata = _reflection.GeneratedProtocolMessageType('UitsMetadata', (_message.Message,), dict(
DESCRIPTOR = _UITSMETADATA,
__module__ = 'uits_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UitsMetadata)
))
_sym_db.RegisterMessage(UitsMetadata)
UitsSignature = _reflection.GeneratedProtocolMessageType('UitsSignature', (_message.Message,), dict(
DESCRIPTOR = _UITSSIGNATURE,
__module__ = 'uits_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UitsSignature)
))
_sym_db.RegisterMessage(UitsSignature)
Uits = _reflection.GeneratedProtocolMessageType('Uits', (_message.Message,), dict(
DESCRIPTOR = _UITS,
__module__ = 'uits_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.Uits)
))
_sym_db.RegisterMessage(Uits)
# @@protoc_insertion_point(module_scope)
gmusicapi-12.1.1/gmusicapi/protocol/upload_pb2.py 0000664 0001750 0001750 00000261123 13374625453 022317 0 ustar simon simon 0000000 0000000 # Generated by the protocol buffer compiler. DO NOT EDIT!
# source: upload.proto
import sys
_b=sys.version_info[0]<3 and (lambda x:x) or (lambda x:x.encode('latin1'))
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 import descriptor_pb2
# @@protoc_insertion_point(imports)
_sym_db = _symbol_database.Default()
from . import locker_pb2 as locker__pb2
DESCRIPTOR = _descriptor.FileDescriptor(
name='upload.proto',
package='wireless_android_skyjam',
syntax='proto2',
serialized_pb=_b('\n\x0cupload.proto\x12\x17wireless_android_skyjam\x1a\x0clocker.proto\"\xb1\x01\n\x0eResponseStatus\x12K\n\rresponse_code\x18\x01 \x02(\x0e\x32\x34.wireless_android_skyjam.ResponseStatus.ResponseCode\"R\n\x0cResponseCode\x12\x06\n\x02OK\x10\x01\x12\x12\n\x0e\x41LREADY_EXISTS\x10\x02\x12\x0e\n\nSOFT_ERROR\x10\x03\x12\x16\n\x12METADATA_TOO_LARGE\x10\x04\"\xa7\x01\n\x0fUploadOperation\x12\x45\n\toperation\x18\x01 \x02(\x0e\x32\x32.wireless_android_skyjam.UploadOperation.Operation\"M\n\tOperation\x12\x14\n\x10OPERATION_CREATE\x10\x01\x12\x14\n\x10OPERATION_MODIFY\x10\x02\x12\x14\n\x10OPERATION_DELETE\x10\x03\"\xa1\x01\n\x11\x45nhancedChallenge\x12P\n\x0ehash_algorithm\x18\x01 \x01(\x0e\x32\x38.wireless_android_skyjam.EnhancedChallenge.HashAlgorithm\x12\x13\n\x0brandom_seed\x18\x02 \x01(\x0c\"%\n\rHashAlgorithm\x12\x0b\n\x07SHA_256\x10\x01\x12\x07\n\x03MD5\x10\x02\"\xac\x01\n\x11\x43lientFpChallenge\x12^\n\x15\x66ingerprint_algorithm\x18\x01 \x01(\x0e\x32?.wireless_android_skyjam.ClientFpChallenge.FingerprintAlgorithm\"7\n\x14\x46ingerprintAlgorithm\x12\x08\n\x04NONE\x10\x00\x12\x15\n\x11LIGHTMP3FP_CLIENT\x10\x01\"\xca\x02\n\rChallengeInfo\x12\x17\n\x0f\x63lient_track_id\x18\x01 \x02(\t\x12\x14\n\x0cstart_millis\x18\x02 \x02(\x05\x12\x17\n\x0f\x64uration_millis\x18\x03 \x02(\x05\x12\x19\n\x11\x63hallenge_user_id\x18\x04 \x01(\t\x12\x1b\n\x13\x63hallenge_timestamp\x18\x05 \x01(\x03\x12\x14\n\x0c\x65xpect_match\x18\x06 \x01(\x08\x12\x46\n\x12\x65nhanced_challenge\x18\x07 \x01(\x0b\x32*.wireless_android_skyjam.EnhancedChallenge\x12\x12\n\ncandidates\x18\x08 \x03(\t\x12G\n\x13\x63lient_fp_challenge\x18\t \x01(\x0b\x32*.wireless_android_skyjam.ClientFpChallenge\"5\n\x19\x45nhancedChallengeResponse\x12\x18\n\x10\x63hallenge_answer\x18\x01 \x01(\x0c\"h\n\x13SignedChallengeInfo\x12>\n\x0e\x63hallenge_info\x18\x01 \x02(\x0b\x32&.wireless_android_skyjam.ChallengeInfo\x12\x11\n\tsignature\x18\x02 \x02(\x0c\"\xbe\x02\n\x0bTrackSample\x12-\n\x05track\x18\x01 \x02(\x0b\x32\x1e.wireless_android_skyjam.Track\x12K\n\x15signed_challenge_info\x18\x02 \x02(\x0b\x32,.wireless_android_skyjam.SignedChallengeInfo\x12\x0e\n\x06sample\x18\x03 \x01(\x0c\x12\x41\n\rsample_format\x18\x04 \x01(\x0e\x32*.wireless_android_skyjam.Track.ContentType\x12;\n\x0euser_album_art\x18\x05 \x01(\x0b\x32#.wireless_android_skyjam.ImageUnion\x12#\n\x1b\x65nhanced_challenge_response\x18\x06 \x01(\x0c\"\xa5\x01\n\x15UploadPlaylistRequest\x12\x42\n\x10upload_operation\x18\x01 \x02(\x0b\x32(.wireless_android_skyjam.UploadOperation\x12\x33\n\x08playlist\x18\x02 \x03(\x0b\x32!.wireless_android_skyjam.Playlist\x12\x13\n\x0buploader_id\x18\x03 \x02(\t\"z\n\x10PlaylistResponse\x12@\n\x0fresponse_status\x18\x01 \x02(\x0b\x32\'.wireless_android_skyjam.ResponseStatus\x12\x11\n\tclient_id\x18\x02 \x01(\t\x12\x11\n\tserver_id\x18\x03 \x01(\t\"^\n\x16UploadPlaylistResponse\x12\x44\n\x11playlist_response\x18\x01 \x03(\x0b\x32).wireless_android_skyjam.PlaylistResponse\"\xb5\x01\n\x1aUploadPlaylistEntryRequest\x12\x42\n\x10upload_operation\x18\x01 \x02(\x0b\x32(.wireless_android_skyjam.UploadOperation\x12>\n\x0eplaylist_entry\x18\x02 \x03(\x0b\x32&.wireless_android_skyjam.PlaylistEntry\x12\x13\n\x0buploader_id\x18\x03 \x02(\t\"\x7f\n\x15PlaylistEntryResponse\x12@\n\x0fresponse_status\x18\x01 \x02(\x0b\x32\'.wireless_android_skyjam.ResponseStatus\x12\x11\n\tclient_id\x18\x02 \x01(\t\x12\x11\n\tserver_id\x18\x03 \x01(\t\"n\n\x1bUploadPlaylistEntryResponse\x12O\n\x17playlist_entry_response\x18\x01 \x03(\x0b\x32..wireless_android_skyjam.PlaylistEntryResponse\"[\n\x15UploadMetadataRequest\x12-\n\x05track\x18\x01 \x03(\x0b\x32\x1e.wireless_android_skyjam.Track\x12\x13\n\x0buploader_id\x18\x02 \x02(\t\"\xb0\x01\n\x18UpdateUploadStateRequest\x12L\n\x05state\x18\x01 \x02(\x0e\x32=.wireless_android_skyjam.UpdateUploadStateRequest.UploadState\x12\x13\n\x0buploader_id\x18\x02 \x02(\t\"1\n\x0bUploadState\x12\t\n\x05START\x10\x01\x12\n\n\x06PAUSED\x10\x02\x12\x0b\n\x07STOPPED\x10\x03\"M\n\x12\x43lientStateRequest\x12\x13\n\x0buploader_id\x18\x01 \x02(\t\x12\"\n\x1aget_purchased_tracks_since\x18\x02 \x01(\x03\"\x80\x02\n\x13\x43lientStateResponse\x12\x1a\n\x12locker_track_limit\x18\x01 \x01(\x03\x12\x1c\n\x14user_songs_in_locker\x18\x02 \x01(\x03\x12\x1e\n\x16track_size_limit_in_mb\x18\x03 \x01(\x05\x12#\n\x1buser_purchased_tracks_since\x18\x04 \x01(\x03\x12\x19\n\x11total_track_count\x18\x05 \x01(\x03\x12O\n\x0foverride_config\x18\x06 \x01(\x0b\x32\x36.wireless_android_skyjam.OverrideConfigValueCollection\"\xc5\x01\n\x16UploadMetadataResponse\x12K\n\x15signed_challenge_info\x18\x01 \x03(\x0b\x32,.wireless_android_skyjam.SignedChallengeInfo\x12\x11\n\tupload_id\x18\x02 \x03(\t\x12K\n\x15track_sample_response\x18\x03 \x03(\x0b\x32,.wireless_android_skyjam.TrackSampleResponse\"\xbd\x03\n\x13TrackSampleResponse\x12\x17\n\x0f\x63lient_track_id\x18\x01 \x02(\t\x12P\n\rresponse_code\x18\x02 \x02(\x0e\x32\x39.wireless_android_skyjam.TrackSampleResponse.ResponseCode\x12\x17\n\x0fserver_track_id\x18\x03 \x01(\t\x12\x15\n\ralbum_art_url\x18\x04 \x01(\t\x12\x14\n\x0c\x65xpect_match\x18\x05 \x01(\x08\"\xf4\x01\n\x0cResponseCode\x12\x0b\n\x07MATCHED\x10\x01\x12\x14\n\x10UPLOAD_REQUESTED\x10\x02\x12\x15\n\x11INVALID_SIGNATURE\x10\x03\x12\x12\n\x0e\x41LREADY_EXISTS\x10\x04\x12\x13\n\x0fTRANSIENT_ERROR\x10\x05\x12\x13\n\x0fPERMANENT_ERROR\x10\x06\x12\x1d\n\x19TRACK_COUNT_LIMIT_REACHED\x10\x07\x12\x16\n\x12REJECT_STORE_TRACK\x10\x08\x12\x1f\n\x1bREJECT_STORE_TRACK_BY_LABEL\x10\t\x12\x14\n\x10REJECT_DRM_TRACK\x10\n\"f\n\x13UploadSampleRequest\x12:\n\x0ctrack_sample\x18\x01 \x03(\x0b\x32$.wireless_android_skyjam.TrackSample\x12\x13\n\x0buploader_id\x18\x02 \x02(\t\"c\n\x14UploadSampleResponse\x12K\n\x15track_sample_response\x18\x01 \x03(\x0b\x32,.wireless_android_skyjam.TrackSampleResponse\";\n\nImageUnion\x12\x16\n\x0euser_album_art\x18\x01 \x01(\x0c\x12\x15\n\ralbum_art_url\x18\x02 \x01(\t\"\xe4\t\n\x0eUploadResponse\x12K\n\rresponse_type\x18\x01 \x01(\x0e\x32\x34.wireless_android_skyjam.UploadResponse.ResponseType\x12J\n\x11metadata_response\x18\x02 \x01(\x0b\x32/.wireless_android_skyjam.UploadMetadataResponse\x12J\n\x11playlist_response\x18\x03 \x01(\x0b\x32/.wireless_android_skyjam.UploadPlaylistResponse\x12U\n\x17playlist_entry_response\x18\x04 \x01(\x0b\x32\x34.wireless_android_skyjam.UploadPlaylistEntryResponse\x12\x46\n\x0fsample_response\x18\x05 \x01(\x0b\x32-.wireless_android_skyjam.UploadSampleResponse\x12\x42\n\x10getjobs_response\x18\x07 \x01(\x0b\x32(.wireless_android_skyjam.GetJobsResponse\x12J\n\x14\x63lientstate_response\x18\x08 \x01(\x0b\x32,.wireless_android_skyjam.ClientStateResponse\x12\x35\n\x06policy\x18\x06 \x01(\x0b\x32%.wireless_android_skyjam.ClientPolicy\x12G\n\x0b\x61uth_status\x18\x0b \x01(\x0e\x32\x32.wireless_android_skyjam.UploadResponse.AuthStatus\x12\x12\n\nauth_error\x18\x0c \x01(\x08\x12@\n\x0fupauth_response\x18\r \x01(\x0b\x32\'.wireless_android_skyjam.UpAuthResponse\"\xfa\x01\n\x0cResponseType\x12\x15\n\x11METADATA_RESPONSE\x10\x01\x12\x15\n\x11PLAYLIST_RESPONSE\x10\x02\x12\x1b\n\x17PLAYLIST_ENTRY_RESPONSE\x10\x03\x12\x13\n\x0fSAMPLE_RESPONSE\x10\x04\x12\x14\n\x10GETJOBS_RESPONSE\x10\x05\x12\x11\n\rAUTH_RESPONSE\x10\x06\x12\x19\n\x15\x43LIENT_STATE_RESPONSE\x10\x07\x12 \n\x1cUPDATE_UPLOAD_STATE_RESPONSE\x10\x08\x12$\n DELETE_UPLOAD_REQUESTED_RESPONSE\x10\t\"\xea\x01\n\nAuthStatus\x12\x06\n\x02OK\x10\x08\x12\x15\n\x11MAX_LIMIT_REACHED\x10\t\x12!\n\x1d\x43LIENT_BOUND_TO_OTHER_ACCOUNT\x10\n\x12\x19\n\x15\x43LIENT_NOT_AUTHORIZED\x10\x0b\x12\"\n\x1eMAX_PER_MACHINE_USERS_EXCEEDED\x10\x0c\x12\x17\n\x13\x43LIENT_PLEASE_RETRY\x10\r\x12\x12\n\x0eNOT_SUBSCRIBED\x10\x0e\x12\x13\n\x0fINVALID_REQUEST\x10\x0f\x12\x19\n\x15UPGRADE_MUSIC_MANAGER\x10\x10\"\xb4\x01\n\x0eTracksToUpload\x12\x11\n\tclient_id\x18\x01 \x02(\t\x12\x11\n\tserver_id\x18\x02 \x02(\t\x12\x43\n\x06status\x18\x05 \x02(\x0e\x32\x33.wireless_android_skyjam.TracksToUpload.TrackStatus\"7\n\x0bTrackStatus\x12\x12\n\x0e\x46ORCE_REUPLOAD\x10\x03\x12\x14\n\x10UPLOAD_REQUESTED\x10\x04\"%\n\x0eGetJobsRequest\x12\x13\n\x0buploader_id\x18\x01 \x02(\t\"p\n\x0fGetJobsResponse\x12\x41\n\x10tracks_to_upload\x18\x01 \x03(\x0b\x32\'.wireless_android_skyjam.TracksToUpload\x12\x1a\n\x12get_tracks_success\x18\x02 \x02(\x08\"l\n\rUpAuthRequest\x12\x13\n\x0buploader_id\x18\x01 \x02(\t\x12\x15\n\rfriendly_name\x18\x02 \x01(\t\x12\x16\n\x0emac_identifier\x18\x03 \x01(\t\x12\x17\n\x0fhash_identifier\x18\x04 \x01(\t\"%\n\x0eUpAuthResponse\x12\x13\n\x0buploader_id\x18\x01 \x01(\t\"3\n\x1c\x44\x65leteUploadRequestedRequest\x12\x13\n\x0buploader_id\x18\x01 \x02(\t\"\x83\x02\n\x0c\x43lientPolicy\x12\x15\n\rpause_uploads\x18\x01 \x01(\x08\x12\r\n\x05\x61\x62ort\x18\x02 \x01(\x08\x12\x18\n\x10retry_in_minutes\x18\x03 \x01(\x05\x12\x1a\n\x12\x62\x61ndwidth_cap_kbps\x18\x04 \x01(\x05\x12\x17\n\x0fpause_downloads\x18\x05 \x01(\x08\x12#\n\x1b\x64ownload_bandwidth_cap_kbps\x18\x06 \x01(\x05\x12\x16\n\x0esend_analytics\x18\x07 \x01(\x08\x12\x15\n\ranalytics_url\x18\x08 \x01(\t\x12\x16\n\x0e\x65nable_gapless\x18\t \x01(\x08\x12\x12\n\nenable_m4p\x18\n \x01(\x08\"p\n\x1dUploadMetadataInternalRequest\x12\x0e\n\x06gaiaid\x18\x01 \x02(\x04\x12?\n\x07request\x18\x02 \x02(\x0b\x32..wireless_android_skyjam.UploadMetadataRequest\"\x9f\x02\n\x13OverrideConfigValue\x12\x0b\n\x03key\x18\x01 \x01(\t\x12\r\n\x05value\x18\x02 \x01(\t\x12O\n\x08priority\x18\x03 \x01(\x0e\x32=.wireless_android_skyjam.OverrideConfigValue.OverridePriority\"\x9a\x01\n\x10OverridePriority\x12\x1d\n\x10\x44\x45\x46\x41ULT_PRIORITY\x10\xff\xff\xff\xff\xff\xff\xff\xff\xff\x01\x12\x13\n\x0fLOWEST_PRIORITY\x10\x64\x12\x11\n\x0cLOW_PRIORITY\x10\xc8\x01\x12\x14\n\x0fMEDIUM_PRIORITY\x10\xac\x02\x12\x12\n\rHIGH_PRIORITY\x10\x90\x03\x12\x15\n\x10HIGHEST_PRIORITY\x10\xf4\x03\"\\\n\x1dOverrideConfigValueCollection\x12;\n\x05value\x18\x01 \x03(\x0b\x32,.wireless_android_skyjam.OverrideConfigValue')
,
dependencies=[locker__pb2.DESCRIPTOR,])
_sym_db.RegisterFileDescriptor(DESCRIPTOR)
_RESPONSESTATUS_RESPONSECODE = _descriptor.EnumDescriptor(
name='ResponseCode',
full_name='wireless_android_skyjam.ResponseStatus.ResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ALREADY_EXISTS', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='SOFT_ERROR', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='METADATA_TOO_LARGE', index=3, number=4,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=151,
serialized_end=233,
)
_sym_db.RegisterEnumDescriptor(_RESPONSESTATUS_RESPONSECODE)
_UPLOADOPERATION_OPERATION = _descriptor.EnumDescriptor(
name='Operation',
full_name='wireless_android_skyjam.UploadOperation.Operation',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OPERATION_CREATE', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='OPERATION_MODIFY', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='OPERATION_DELETE', index=2, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=326,
serialized_end=403,
)
_sym_db.RegisterEnumDescriptor(_UPLOADOPERATION_OPERATION)
_ENHANCEDCHALLENGE_HASHALGORITHM = _descriptor.EnumDescriptor(
name='HashAlgorithm',
full_name='wireless_android_skyjam.EnhancedChallenge.HashAlgorithm',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='SHA_256', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MD5', index=1, number=2,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=530,
serialized_end=567,
)
_sym_db.RegisterEnumDescriptor(_ENHANCEDCHALLENGE_HASHALGORITHM)
_CLIENTFPCHALLENGE_FINGERPRINTALGORITHM = _descriptor.EnumDescriptor(
name='FingerprintAlgorithm',
full_name='wireless_android_skyjam.ClientFpChallenge.FingerprintAlgorithm',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='NONE', index=0, number=0,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='LIGHTMP3FP_CLIENT', index=1, number=1,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=687,
serialized_end=742,
)
_sym_db.RegisterEnumDescriptor(_CLIENTFPCHALLENGE_FINGERPRINTALGORITHM)
_UPDATEUPLOADSTATEREQUEST_UPLOADSTATE = _descriptor.EnumDescriptor(
name='UploadState',
full_name='wireless_android_skyjam.UpdateUploadStateRequest.UploadState',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='START', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PAUSED', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='STOPPED', index=2, number=3,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=2593,
serialized_end=2642,
)
_sym_db.RegisterEnumDescriptor(_UPDATEUPLOADSTATEREQUEST_UPLOADSTATE)
_TRACKSAMPLERESPONSE_RESPONSECODE = _descriptor.EnumDescriptor(
name='ResponseCode',
full_name='wireless_android_skyjam.TrackSampleResponse.ResponseCode',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='MATCHED', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UPLOAD_REQUESTED', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='INVALID_SIGNATURE', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='ALREADY_EXISTS', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='TRANSIENT_ERROR', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PERMANENT_ERROR', index=5, number=6,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='TRACK_COUNT_LIMIT_REACHED', index=6, number=7,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='REJECT_STORE_TRACK', index=7, number=8,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='REJECT_STORE_TRACK_BY_LABEL', index=8, number=9,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='REJECT_DRM_TRACK', index=9, number=10,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=3384,
serialized_end=3628,
)
_sym_db.RegisterEnumDescriptor(_TRACKSAMPLERESPONSE_RESPONSECODE)
_UPLOADRESPONSE_RESPONSETYPE = _descriptor.EnumDescriptor(
name='ResponseType',
full_name='wireless_android_skyjam.UploadResponse.ResponseType',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='METADATA_RESPONSE', index=0, number=1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PLAYLIST_RESPONSE', index=1, number=2,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='PLAYLIST_ENTRY_RESPONSE', index=2, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='SAMPLE_RESPONSE', index=3, number=4,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='GETJOBS_RESPONSE', index=4, number=5,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='AUTH_RESPONSE', index=5, number=6,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_STATE_RESPONSE', index=6, number=7,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UPDATE_UPLOAD_STATE_RESPONSE', index=7, number=8,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='DELETE_UPLOAD_REQUESTED_RESPONSE', index=8, number=9,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=4662,
serialized_end=4912,
)
_sym_db.RegisterEnumDescriptor(_UPLOADRESPONSE_RESPONSETYPE)
_UPLOADRESPONSE_AUTHSTATUS = _descriptor.EnumDescriptor(
name='AuthStatus',
full_name='wireless_android_skyjam.UploadResponse.AuthStatus',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='OK', index=0, number=8,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MAX_LIMIT_REACHED', index=1, number=9,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_BOUND_TO_OTHER_ACCOUNT', index=2, number=10,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_NOT_AUTHORIZED', index=3, number=11,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MAX_PER_MACHINE_USERS_EXCEEDED', index=4, number=12,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='CLIENT_PLEASE_RETRY', index=5, number=13,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='NOT_SUBSCRIBED', index=6, number=14,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='INVALID_REQUEST', index=7, number=15,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UPGRADE_MUSIC_MANAGER', index=8, number=16,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=4915,
serialized_end=5149,
)
_sym_db.RegisterEnumDescriptor(_UPLOADRESPONSE_AUTHSTATUS)
_TRACKSTOUPLOAD_TRACKSTATUS = _descriptor.EnumDescriptor(
name='TrackStatus',
full_name='wireless_android_skyjam.TracksToUpload.TrackStatus',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='FORCE_REUPLOAD', index=0, number=3,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='UPLOAD_REQUESTED', index=1, number=4,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=5277,
serialized_end=5332,
)
_sym_db.RegisterEnumDescriptor(_TRACKSTOUPLOAD_TRACKSTATUS)
_OVERRIDECONFIGVALUE_OVERRIDEPRIORITY = _descriptor.EnumDescriptor(
name='OverridePriority',
full_name='wireless_android_skyjam.OverrideConfigValue.OverridePriority',
filename=None,
file=DESCRIPTOR,
values=[
_descriptor.EnumValueDescriptor(
name='DEFAULT_PRIORITY', index=0, number=-1,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='LOWEST_PRIORITY', index=1, number=100,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='LOW_PRIORITY', index=2, number=200,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='MEDIUM_PRIORITY', index=3, number=300,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='HIGH_PRIORITY', index=4, number=400,
options=None,
type=None),
_descriptor.EnumValueDescriptor(
name='HIGHEST_PRIORITY', index=5, number=500,
options=None,
type=None),
],
containing_type=None,
options=None,
serialized_start=6199,
serialized_end=6353,
)
_sym_db.RegisterEnumDescriptor(_OVERRIDECONFIGVALUE_OVERRIDEPRIORITY)
_RESPONSESTATUS = _descriptor.Descriptor(
name='ResponseStatus',
full_name='wireless_android_skyjam.ResponseStatus',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.ResponseStatus.response_code', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_RESPONSESTATUS_RESPONSECODE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=56,
serialized_end=233,
)
_UPLOADOPERATION = _descriptor.Descriptor(
name='UploadOperation',
full_name='wireless_android_skyjam.UploadOperation',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='operation', full_name='wireless_android_skyjam.UploadOperation.operation', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_UPLOADOPERATION_OPERATION,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=236,
serialized_end=403,
)
_ENHANCEDCHALLENGE = _descriptor.Descriptor(
name='EnhancedChallenge',
full_name='wireless_android_skyjam.EnhancedChallenge',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='hash_algorithm', full_name='wireless_android_skyjam.EnhancedChallenge.hash_algorithm', index=0,
number=1, 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,
options=None),
_descriptor.FieldDescriptor(
name='random_seed', full_name='wireless_android_skyjam.EnhancedChallenge.random_seed', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_ENHANCEDCHALLENGE_HASHALGORITHM,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=406,
serialized_end=567,
)
_CLIENTFPCHALLENGE = _descriptor.Descriptor(
name='ClientFpChallenge',
full_name='wireless_android_skyjam.ClientFpChallenge',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='fingerprint_algorithm', full_name='wireless_android_skyjam.ClientFpChallenge.fingerprint_algorithm', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_CLIENTFPCHALLENGE_FINGERPRINTALGORITHM,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=570,
serialized_end=742,
)
_CHALLENGEINFO = _descriptor.Descriptor(
name='ChallengeInfo',
full_name='wireless_android_skyjam.ChallengeInfo',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='client_track_id', full_name='wireless_android_skyjam.ChallengeInfo.client_track_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='start_millis', full_name='wireless_android_skyjam.ChallengeInfo.start_millis', index=1,
number=2, type=5, cpp_type=1, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='duration_millis', full_name='wireless_android_skyjam.ChallengeInfo.duration_millis', index=2,
number=3, type=5, cpp_type=1, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='challenge_user_id', full_name='wireless_android_skyjam.ChallengeInfo.challenge_user_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='challenge_timestamp', full_name='wireless_android_skyjam.ChallengeInfo.challenge_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,
options=None),
_descriptor.FieldDescriptor(
name='expect_match', full_name='wireless_android_skyjam.ChallengeInfo.expect_match', 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,
options=None),
_descriptor.FieldDescriptor(
name='enhanced_challenge', full_name='wireless_android_skyjam.ChallengeInfo.enhanced_challenge', 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,
options=None),
_descriptor.FieldDescriptor(
name='candidates', full_name='wireless_android_skyjam.ChallengeInfo.candidates', 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,
options=None),
_descriptor.FieldDescriptor(
name='client_fp_challenge', full_name='wireless_android_skyjam.ChallengeInfo.client_fp_challenge', index=8,
number=9, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=745,
serialized_end=1075,
)
_ENHANCEDCHALLENGERESPONSE = _descriptor.Descriptor(
name='EnhancedChallengeResponse',
full_name='wireless_android_skyjam.EnhancedChallengeResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='challenge_answer', full_name='wireless_android_skyjam.EnhancedChallengeResponse.challenge_answer', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=1077,
serialized_end=1130,
)
_SIGNEDCHALLENGEINFO = _descriptor.Descriptor(
name='SignedChallengeInfo',
full_name='wireless_android_skyjam.SignedChallengeInfo',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='challenge_info', full_name='wireless_android_skyjam.SignedChallengeInfo.challenge_info', index=0,
number=1, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='signature', full_name='wireless_android_skyjam.SignedChallengeInfo.signature', index=1,
number=2, type=12, cpp_type=9, label=2,
has_default_value=False, default_value=_b(""),
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=1132,
serialized_end=1236,
)
_TRACKSAMPLE = _descriptor.Descriptor(
name='TrackSample',
full_name='wireless_android_skyjam.TrackSample',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='track', full_name='wireless_android_skyjam.TrackSample.track', index=0,
number=1, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='signed_challenge_info', full_name='wireless_android_skyjam.TrackSample.signed_challenge_info', index=1,
number=2, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='sample', full_name='wireless_android_skyjam.TrackSample.sample', 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,
options=None),
_descriptor.FieldDescriptor(
name='sample_format', full_name='wireless_android_skyjam.TrackSample.sample_format', index=3,
number=4, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='user_album_art', full_name='wireless_android_skyjam.TrackSample.user_album_art', 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,
options=None),
_descriptor.FieldDescriptor(
name='enhanced_challenge_response', full_name='wireless_android_skyjam.TrackSample.enhanced_challenge_response', index=5,
number=6, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=1239,
serialized_end=1557,
)
_UPLOADPLAYLISTREQUEST = _descriptor.Descriptor(
name='UploadPlaylistRequest',
full_name='wireless_android_skyjam.UploadPlaylistRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='upload_operation', full_name='wireless_android_skyjam.UploadPlaylistRequest.upload_operation', index=0,
number=1, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist', full_name='wireless_android_skyjam.UploadPlaylistRequest.playlist', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.UploadPlaylistRequest.uploader_id', index=2,
number=3, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=1560,
serialized_end=1725,
)
_PLAYLISTRESPONSE = _descriptor.Descriptor(
name='PlaylistResponse',
full_name='wireless_android_skyjam.PlaylistResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_status', full_name='wireless_android_skyjam.PlaylistResponse.response_status', index=0,
number=1, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.PlaylistResponse.client_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,
options=None),
_descriptor.FieldDescriptor(
name='server_id', full_name='wireless_android_skyjam.PlaylistResponse.server_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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=1727,
serialized_end=1849,
)
_UPLOADPLAYLISTRESPONSE = _descriptor.Descriptor(
name='UploadPlaylistResponse',
full_name='wireless_android_skyjam.UploadPlaylistResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='playlist_response', full_name='wireless_android_skyjam.UploadPlaylistResponse.playlist_response', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=1851,
serialized_end=1945,
)
_UPLOADPLAYLISTENTRYREQUEST = _descriptor.Descriptor(
name='UploadPlaylistEntryRequest',
full_name='wireless_android_skyjam.UploadPlaylistEntryRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='upload_operation', full_name='wireless_android_skyjam.UploadPlaylistEntryRequest.upload_operation', index=0,
number=1, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entry', full_name='wireless_android_skyjam.UploadPlaylistEntryRequest.playlist_entry', index=1,
number=2, type=11, cpp_type=10, label=3,
has_default_value=False, default_value=[],
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.UploadPlaylistEntryRequest.uploader_id', index=2,
number=3, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=1948,
serialized_end=2129,
)
_PLAYLISTENTRYRESPONSE = _descriptor.Descriptor(
name='PlaylistEntryResponse',
full_name='wireless_android_skyjam.PlaylistEntryResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_status', full_name='wireless_android_skyjam.PlaylistEntryResponse.response_status', index=0,
number=1, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.PlaylistEntryResponse.client_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,
options=None),
_descriptor.FieldDescriptor(
name='server_id', full_name='wireless_android_skyjam.PlaylistEntryResponse.server_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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=2131,
serialized_end=2258,
)
_UPLOADPLAYLISTENTRYRESPONSE = _descriptor.Descriptor(
name='UploadPlaylistEntryResponse',
full_name='wireless_android_skyjam.UploadPlaylistEntryResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='playlist_entry_response', full_name='wireless_android_skyjam.UploadPlaylistEntryResponse.playlist_entry_response', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=2260,
serialized_end=2370,
)
_UPLOADMETADATAREQUEST = _descriptor.Descriptor(
name='UploadMetadataRequest',
full_name='wireless_android_skyjam.UploadMetadataRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='track', full_name='wireless_android_skyjam.UploadMetadataRequest.track', 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,
options=None),
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.UploadMetadataRequest.uploader_id', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=2372,
serialized_end=2463,
)
_UPDATEUPLOADSTATEREQUEST = _descriptor.Descriptor(
name='UpdateUploadStateRequest',
full_name='wireless_android_skyjam.UpdateUploadStateRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='state', full_name='wireless_android_skyjam.UpdateUploadStateRequest.state', index=0,
number=1, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.UpdateUploadStateRequest.uploader_id', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_UPDATEUPLOADSTATEREQUEST_UPLOADSTATE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=2466,
serialized_end=2642,
)
_CLIENTSTATEREQUEST = _descriptor.Descriptor(
name='ClientStateRequest',
full_name='wireless_android_skyjam.ClientStateRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.ClientStateRequest.uploader_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='get_purchased_tracks_since', full_name='wireless_android_skyjam.ClientStateRequest.get_purchased_tracks_since', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=2644,
serialized_end=2721,
)
_CLIENTSTATERESPONSE = _descriptor.Descriptor(
name='ClientStateResponse',
full_name='wireless_android_skyjam.ClientStateResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='locker_track_limit', full_name='wireless_android_skyjam.ClientStateResponse.locker_track_limit', 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,
options=None),
_descriptor.FieldDescriptor(
name='user_songs_in_locker', full_name='wireless_android_skyjam.ClientStateResponse.user_songs_in_locker', 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,
options=None),
_descriptor.FieldDescriptor(
name='track_size_limit_in_mb', full_name='wireless_android_skyjam.ClientStateResponse.track_size_limit_in_mb', 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,
options=None),
_descriptor.FieldDescriptor(
name='user_purchased_tracks_since', full_name='wireless_android_skyjam.ClientStateResponse.user_purchased_tracks_since', index=3,
number=4, type=3, cpp_type=2, label=1,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='total_track_count', full_name='wireless_android_skyjam.ClientStateResponse.total_track_count', 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,
options=None),
_descriptor.FieldDescriptor(
name='override_config', full_name='wireless_android_skyjam.ClientStateResponse.override_config', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=2724,
serialized_end=2980,
)
_UPLOADMETADATARESPONSE = _descriptor.Descriptor(
name='UploadMetadataResponse',
full_name='wireless_android_skyjam.UploadMetadataResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='signed_challenge_info', full_name='wireless_android_skyjam.UploadMetadataResponse.signed_challenge_info', 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,
options=None),
_descriptor.FieldDescriptor(
name='upload_id', full_name='wireless_android_skyjam.UploadMetadataResponse.upload_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='track_sample_response', full_name='wireless_android_skyjam.UploadMetadataResponse.track_sample_response', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=2983,
serialized_end=3180,
)
_TRACKSAMPLERESPONSE = _descriptor.Descriptor(
name='TrackSampleResponse',
full_name='wireless_android_skyjam.TrackSampleResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='client_track_id', full_name='wireless_android_skyjam.TrackSampleResponse.client_track_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='response_code', full_name='wireless_android_skyjam.TrackSampleResponse.response_code', index=1,
number=2, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=1,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='server_track_id', full_name='wireless_android_skyjam.TrackSampleResponse.server_track_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,
options=None),
_descriptor.FieldDescriptor(
name='album_art_url', full_name='wireless_android_skyjam.TrackSampleResponse.album_art_url', 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,
options=None),
_descriptor.FieldDescriptor(
name='expect_match', full_name='wireless_android_skyjam.TrackSampleResponse.expect_match', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_TRACKSAMPLERESPONSE_RESPONSECODE,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=3183,
serialized_end=3628,
)
_UPLOADSAMPLEREQUEST = _descriptor.Descriptor(
name='UploadSampleRequest',
full_name='wireless_android_skyjam.UploadSampleRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='track_sample', full_name='wireless_android_skyjam.UploadSampleRequest.track_sample', 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,
options=None),
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.UploadSampleRequest.uploader_id', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=3630,
serialized_end=3732,
)
_UPLOADSAMPLERESPONSE = _descriptor.Descriptor(
name='UploadSampleResponse',
full_name='wireless_android_skyjam.UploadSampleResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='track_sample_response', full_name='wireless_android_skyjam.UploadSampleResponse.track_sample_response', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=3734,
serialized_end=3833,
)
_IMAGEUNION = _descriptor.Descriptor(
name='ImageUnion',
full_name='wireless_android_skyjam.ImageUnion',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='user_album_art', full_name='wireless_android_skyjam.ImageUnion.user_album_art', 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,
options=None),
_descriptor.FieldDescriptor(
name='album_art_url', full_name='wireless_android_skyjam.ImageUnion.album_art_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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=3835,
serialized_end=3894,
)
_UPLOADRESPONSE = _descriptor.Descriptor(
name='UploadResponse',
full_name='wireless_android_skyjam.UploadResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='response_type', full_name='wireless_android_skyjam.UploadResponse.response_type', index=0,
number=1, 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,
options=None),
_descriptor.FieldDescriptor(
name='metadata_response', full_name='wireless_android_skyjam.UploadResponse.metadata_response', 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,
options=None),
_descriptor.FieldDescriptor(
name='playlist_response', full_name='wireless_android_skyjam.UploadResponse.playlist_response', 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,
options=None),
_descriptor.FieldDescriptor(
name='playlist_entry_response', full_name='wireless_android_skyjam.UploadResponse.playlist_entry_response', 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,
options=None),
_descriptor.FieldDescriptor(
name='sample_response', full_name='wireless_android_skyjam.UploadResponse.sample_response', 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,
options=None),
_descriptor.FieldDescriptor(
name='getjobs_response', full_name='wireless_android_skyjam.UploadResponse.getjobs_response', index=5,
number=7, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='clientstate_response', full_name='wireless_android_skyjam.UploadResponse.clientstate_response', index=6,
number=8, type=11, cpp_type=10, label=1,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='policy', full_name='wireless_android_skyjam.UploadResponse.policy', index=7,
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,
options=None),
_descriptor.FieldDescriptor(
name='auth_status', full_name='wireless_android_skyjam.UploadResponse.auth_status', index=8,
number=11, type=14, cpp_type=8, label=1,
has_default_value=False, default_value=8,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='auth_error', full_name='wireless_android_skyjam.UploadResponse.auth_error', index=9,
number=12, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='upauth_response', full_name='wireless_android_skyjam.UploadResponse.upauth_response', index=10,
number=13, 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_UPLOADRESPONSE_RESPONSETYPE,
_UPLOADRESPONSE_AUTHSTATUS,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=3897,
serialized_end=5149,
)
_TRACKSTOUPLOAD = _descriptor.Descriptor(
name='TracksToUpload',
full_name='wireless_android_skyjam.TracksToUpload',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='client_id', full_name='wireless_android_skyjam.TracksToUpload.client_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='server_id', full_name='wireless_android_skyjam.TracksToUpload.server_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='status', full_name='wireless_android_skyjam.TracksToUpload.status', index=2,
number=5, type=14, cpp_type=8, label=2,
has_default_value=False, default_value=3,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_TRACKSTOUPLOAD_TRACKSTATUS,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5152,
serialized_end=5332,
)
_GETJOBSREQUEST = _descriptor.Descriptor(
name='GetJobsRequest',
full_name='wireless_android_skyjam.GetJobsRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.GetJobsRequest.uploader_id', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5334,
serialized_end=5371,
)
_GETJOBSRESPONSE = _descriptor.Descriptor(
name='GetJobsResponse',
full_name='wireless_android_skyjam.GetJobsResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='tracks_to_upload', full_name='wireless_android_skyjam.GetJobsResponse.tracks_to_upload', 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,
options=None),
_descriptor.FieldDescriptor(
name='get_tracks_success', full_name='wireless_android_skyjam.GetJobsResponse.get_tracks_success', index=1,
number=2, type=8, cpp_type=7, label=2,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5373,
serialized_end=5485,
)
_UPAUTHREQUEST = _descriptor.Descriptor(
name='UpAuthRequest',
full_name='wireless_android_skyjam.UpAuthRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.UpAuthRequest.uploader_id', 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,
options=None),
_descriptor.FieldDescriptor(
name='friendly_name', full_name='wireless_android_skyjam.UpAuthRequest.friendly_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,
options=None),
_descriptor.FieldDescriptor(
name='mac_identifier', full_name='wireless_android_skyjam.UpAuthRequest.mac_identifier', 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,
options=None),
_descriptor.FieldDescriptor(
name='hash_identifier', full_name='wireless_android_skyjam.UpAuthRequest.hash_identifier', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5487,
serialized_end=5595,
)
_UPAUTHRESPONSE = _descriptor.Descriptor(
name='UpAuthResponse',
full_name='wireless_android_skyjam.UpAuthResponse',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.UpAuthResponse.uploader_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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5597,
serialized_end=5634,
)
_DELETEUPLOADREQUESTEDREQUEST = _descriptor.Descriptor(
name='DeleteUploadRequestedRequest',
full_name='wireless_android_skyjam.DeleteUploadRequestedRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='uploader_id', full_name='wireless_android_skyjam.DeleteUploadRequestedRequest.uploader_id', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5636,
serialized_end=5687,
)
_CLIENTPOLICY = _descriptor.Descriptor(
name='ClientPolicy',
full_name='wireless_android_skyjam.ClientPolicy',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='pause_uploads', full_name='wireless_android_skyjam.ClientPolicy.pause_uploads', 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,
options=None),
_descriptor.FieldDescriptor(
name='abort', full_name='wireless_android_skyjam.ClientPolicy.abort', 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,
options=None),
_descriptor.FieldDescriptor(
name='retry_in_minutes', full_name='wireless_android_skyjam.ClientPolicy.retry_in_minutes', 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,
options=None),
_descriptor.FieldDescriptor(
name='bandwidth_cap_kbps', full_name='wireless_android_skyjam.ClientPolicy.bandwidth_cap_kbps', 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,
options=None),
_descriptor.FieldDescriptor(
name='pause_downloads', full_name='wireless_android_skyjam.ClientPolicy.pause_downloads', 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,
options=None),
_descriptor.FieldDescriptor(
name='download_bandwidth_cap_kbps', full_name='wireless_android_skyjam.ClientPolicy.download_bandwidth_cap_kbps', index=5,
number=6, 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,
options=None),
_descriptor.FieldDescriptor(
name='send_analytics', full_name='wireless_android_skyjam.ClientPolicy.send_analytics', 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,
options=None),
_descriptor.FieldDescriptor(
name='analytics_url', full_name='wireless_android_skyjam.ClientPolicy.analytics_url', 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,
options=None),
_descriptor.FieldDescriptor(
name='enable_gapless', full_name='wireless_android_skyjam.ClientPolicy.enable_gapless', index=8,
number=9, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='enable_m4p', full_name='wireless_android_skyjam.ClientPolicy.enable_m4p', index=9,
number=10, type=8, cpp_type=7, label=1,
has_default_value=False, default_value=False,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5690,
serialized_end=5949,
)
_UPLOADMETADATAINTERNALREQUEST = _descriptor.Descriptor(
name='UploadMetadataInternalRequest',
full_name='wireless_android_skyjam.UploadMetadataInternalRequest',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='gaiaid', full_name='wireless_android_skyjam.UploadMetadataInternalRequest.gaiaid', index=0,
number=1, type=4, cpp_type=4, label=2,
has_default_value=False, default_value=0,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
_descriptor.FieldDescriptor(
name='request', full_name='wireless_android_skyjam.UploadMetadataInternalRequest.request', index=1,
number=2, type=11, cpp_type=10, label=2,
has_default_value=False, default_value=None,
message_type=None, enum_type=None, containing_type=None,
is_extension=False, extension_scope=None,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=5951,
serialized_end=6063,
)
_OVERRIDECONFIGVALUE = _descriptor.Descriptor(
name='OverrideConfigValue',
full_name='wireless_android_skyjam.OverrideConfigValue',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='key', full_name='wireless_android_skyjam.OverrideConfigValue.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,
options=None),
_descriptor.FieldDescriptor(
name='value', full_name='wireless_android_skyjam.OverrideConfigValue.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,
options=None),
_descriptor.FieldDescriptor(
name='priority', full_name='wireless_android_skyjam.OverrideConfigValue.priority', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
_OVERRIDECONFIGVALUE_OVERRIDEPRIORITY,
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=6066,
serialized_end=6353,
)
_OVERRIDECONFIGVALUECOLLECTION = _descriptor.Descriptor(
name='OverrideConfigValueCollection',
full_name='wireless_android_skyjam.OverrideConfigValueCollection',
filename=None,
file=DESCRIPTOR,
containing_type=None,
fields=[
_descriptor.FieldDescriptor(
name='value', full_name='wireless_android_skyjam.OverrideConfigValueCollection.value', 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,
options=None),
],
extensions=[
],
nested_types=[],
enum_types=[
],
options=None,
is_extendable=False,
syntax='proto2',
extension_ranges=[],
oneofs=[
],
serialized_start=6355,
serialized_end=6447,
)
_RESPONSESTATUS.fields_by_name['response_code'].enum_type = _RESPONSESTATUS_RESPONSECODE
_RESPONSESTATUS_RESPONSECODE.containing_type = _RESPONSESTATUS
_UPLOADOPERATION.fields_by_name['operation'].enum_type = _UPLOADOPERATION_OPERATION
_UPLOADOPERATION_OPERATION.containing_type = _UPLOADOPERATION
_ENHANCEDCHALLENGE.fields_by_name['hash_algorithm'].enum_type = _ENHANCEDCHALLENGE_HASHALGORITHM
_ENHANCEDCHALLENGE_HASHALGORITHM.containing_type = _ENHANCEDCHALLENGE
_CLIENTFPCHALLENGE.fields_by_name['fingerprint_algorithm'].enum_type = _CLIENTFPCHALLENGE_FINGERPRINTALGORITHM
_CLIENTFPCHALLENGE_FINGERPRINTALGORITHM.containing_type = _CLIENTFPCHALLENGE
_CHALLENGEINFO.fields_by_name['enhanced_challenge'].message_type = _ENHANCEDCHALLENGE
_CHALLENGEINFO.fields_by_name['client_fp_challenge'].message_type = _CLIENTFPCHALLENGE
_SIGNEDCHALLENGEINFO.fields_by_name['challenge_info'].message_type = _CHALLENGEINFO
_TRACKSAMPLE.fields_by_name['track'].message_type = locker__pb2._TRACK
_TRACKSAMPLE.fields_by_name['signed_challenge_info'].message_type = _SIGNEDCHALLENGEINFO
_TRACKSAMPLE.fields_by_name['sample_format'].enum_type = locker__pb2._TRACK_CONTENTTYPE
_TRACKSAMPLE.fields_by_name['user_album_art'].message_type = _IMAGEUNION
_UPLOADPLAYLISTREQUEST.fields_by_name['upload_operation'].message_type = _UPLOADOPERATION
_UPLOADPLAYLISTREQUEST.fields_by_name['playlist'].message_type = locker__pb2._PLAYLIST
_PLAYLISTRESPONSE.fields_by_name['response_status'].message_type = _RESPONSESTATUS
_UPLOADPLAYLISTRESPONSE.fields_by_name['playlist_response'].message_type = _PLAYLISTRESPONSE
_UPLOADPLAYLISTENTRYREQUEST.fields_by_name['upload_operation'].message_type = _UPLOADOPERATION
_UPLOADPLAYLISTENTRYREQUEST.fields_by_name['playlist_entry'].message_type = locker__pb2._PLAYLISTENTRY
_PLAYLISTENTRYRESPONSE.fields_by_name['response_status'].message_type = _RESPONSESTATUS
_UPLOADPLAYLISTENTRYRESPONSE.fields_by_name['playlist_entry_response'].message_type = _PLAYLISTENTRYRESPONSE
_UPLOADMETADATAREQUEST.fields_by_name['track'].message_type = locker__pb2._TRACK
_UPDATEUPLOADSTATEREQUEST.fields_by_name['state'].enum_type = _UPDATEUPLOADSTATEREQUEST_UPLOADSTATE
_UPDATEUPLOADSTATEREQUEST_UPLOADSTATE.containing_type = _UPDATEUPLOADSTATEREQUEST
_CLIENTSTATERESPONSE.fields_by_name['override_config'].message_type = _OVERRIDECONFIGVALUECOLLECTION
_UPLOADMETADATARESPONSE.fields_by_name['signed_challenge_info'].message_type = _SIGNEDCHALLENGEINFO
_UPLOADMETADATARESPONSE.fields_by_name['track_sample_response'].message_type = _TRACKSAMPLERESPONSE
_TRACKSAMPLERESPONSE.fields_by_name['response_code'].enum_type = _TRACKSAMPLERESPONSE_RESPONSECODE
_TRACKSAMPLERESPONSE_RESPONSECODE.containing_type = _TRACKSAMPLERESPONSE
_UPLOADSAMPLEREQUEST.fields_by_name['track_sample'].message_type = _TRACKSAMPLE
_UPLOADSAMPLERESPONSE.fields_by_name['track_sample_response'].message_type = _TRACKSAMPLERESPONSE
_UPLOADRESPONSE.fields_by_name['response_type'].enum_type = _UPLOADRESPONSE_RESPONSETYPE
_UPLOADRESPONSE.fields_by_name['metadata_response'].message_type = _UPLOADMETADATARESPONSE
_UPLOADRESPONSE.fields_by_name['playlist_response'].message_type = _UPLOADPLAYLISTRESPONSE
_UPLOADRESPONSE.fields_by_name['playlist_entry_response'].message_type = _UPLOADPLAYLISTENTRYRESPONSE
_UPLOADRESPONSE.fields_by_name['sample_response'].message_type = _UPLOADSAMPLERESPONSE
_UPLOADRESPONSE.fields_by_name['getjobs_response'].message_type = _GETJOBSRESPONSE
_UPLOADRESPONSE.fields_by_name['clientstate_response'].message_type = _CLIENTSTATERESPONSE
_UPLOADRESPONSE.fields_by_name['policy'].message_type = _CLIENTPOLICY
_UPLOADRESPONSE.fields_by_name['auth_status'].enum_type = _UPLOADRESPONSE_AUTHSTATUS
_UPLOADRESPONSE.fields_by_name['upauth_response'].message_type = _UPAUTHRESPONSE
_UPLOADRESPONSE_RESPONSETYPE.containing_type = _UPLOADRESPONSE
_UPLOADRESPONSE_AUTHSTATUS.containing_type = _UPLOADRESPONSE
_TRACKSTOUPLOAD.fields_by_name['status'].enum_type = _TRACKSTOUPLOAD_TRACKSTATUS
_TRACKSTOUPLOAD_TRACKSTATUS.containing_type = _TRACKSTOUPLOAD
_GETJOBSRESPONSE.fields_by_name['tracks_to_upload'].message_type = _TRACKSTOUPLOAD
_UPLOADMETADATAINTERNALREQUEST.fields_by_name['request'].message_type = _UPLOADMETADATAREQUEST
_OVERRIDECONFIGVALUE.fields_by_name['priority'].enum_type = _OVERRIDECONFIGVALUE_OVERRIDEPRIORITY
_OVERRIDECONFIGVALUE_OVERRIDEPRIORITY.containing_type = _OVERRIDECONFIGVALUE
_OVERRIDECONFIGVALUECOLLECTION.fields_by_name['value'].message_type = _OVERRIDECONFIGVALUE
DESCRIPTOR.message_types_by_name['ResponseStatus'] = _RESPONSESTATUS
DESCRIPTOR.message_types_by_name['UploadOperation'] = _UPLOADOPERATION
DESCRIPTOR.message_types_by_name['EnhancedChallenge'] = _ENHANCEDCHALLENGE
DESCRIPTOR.message_types_by_name['ClientFpChallenge'] = _CLIENTFPCHALLENGE
DESCRIPTOR.message_types_by_name['ChallengeInfo'] = _CHALLENGEINFO
DESCRIPTOR.message_types_by_name['EnhancedChallengeResponse'] = _ENHANCEDCHALLENGERESPONSE
DESCRIPTOR.message_types_by_name['SignedChallengeInfo'] = _SIGNEDCHALLENGEINFO
DESCRIPTOR.message_types_by_name['TrackSample'] = _TRACKSAMPLE
DESCRIPTOR.message_types_by_name['UploadPlaylistRequest'] = _UPLOADPLAYLISTREQUEST
DESCRIPTOR.message_types_by_name['PlaylistResponse'] = _PLAYLISTRESPONSE
DESCRIPTOR.message_types_by_name['UploadPlaylistResponse'] = _UPLOADPLAYLISTRESPONSE
DESCRIPTOR.message_types_by_name['UploadPlaylistEntryRequest'] = _UPLOADPLAYLISTENTRYREQUEST
DESCRIPTOR.message_types_by_name['PlaylistEntryResponse'] = _PLAYLISTENTRYRESPONSE
DESCRIPTOR.message_types_by_name['UploadPlaylistEntryResponse'] = _UPLOADPLAYLISTENTRYRESPONSE
DESCRIPTOR.message_types_by_name['UploadMetadataRequest'] = _UPLOADMETADATAREQUEST
DESCRIPTOR.message_types_by_name['UpdateUploadStateRequest'] = _UPDATEUPLOADSTATEREQUEST
DESCRIPTOR.message_types_by_name['ClientStateRequest'] = _CLIENTSTATEREQUEST
DESCRIPTOR.message_types_by_name['ClientStateResponse'] = _CLIENTSTATERESPONSE
DESCRIPTOR.message_types_by_name['UploadMetadataResponse'] = _UPLOADMETADATARESPONSE
DESCRIPTOR.message_types_by_name['TrackSampleResponse'] = _TRACKSAMPLERESPONSE
DESCRIPTOR.message_types_by_name['UploadSampleRequest'] = _UPLOADSAMPLEREQUEST
DESCRIPTOR.message_types_by_name['UploadSampleResponse'] = _UPLOADSAMPLERESPONSE
DESCRIPTOR.message_types_by_name['ImageUnion'] = _IMAGEUNION
DESCRIPTOR.message_types_by_name['UploadResponse'] = _UPLOADRESPONSE
DESCRIPTOR.message_types_by_name['TracksToUpload'] = _TRACKSTOUPLOAD
DESCRIPTOR.message_types_by_name['GetJobsRequest'] = _GETJOBSREQUEST
DESCRIPTOR.message_types_by_name['GetJobsResponse'] = _GETJOBSRESPONSE
DESCRIPTOR.message_types_by_name['UpAuthRequest'] = _UPAUTHREQUEST
DESCRIPTOR.message_types_by_name['UpAuthResponse'] = _UPAUTHRESPONSE
DESCRIPTOR.message_types_by_name['DeleteUploadRequestedRequest'] = _DELETEUPLOADREQUESTEDREQUEST
DESCRIPTOR.message_types_by_name['ClientPolicy'] = _CLIENTPOLICY
DESCRIPTOR.message_types_by_name['UploadMetadataInternalRequest'] = _UPLOADMETADATAINTERNALREQUEST
DESCRIPTOR.message_types_by_name['OverrideConfigValue'] = _OVERRIDECONFIGVALUE
DESCRIPTOR.message_types_by_name['OverrideConfigValueCollection'] = _OVERRIDECONFIGVALUECOLLECTION
ResponseStatus = _reflection.GeneratedProtocolMessageType('ResponseStatus', (_message.Message,), dict(
DESCRIPTOR = _RESPONSESTATUS,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.ResponseStatus)
))
_sym_db.RegisterMessage(ResponseStatus)
UploadOperation = _reflection.GeneratedProtocolMessageType('UploadOperation', (_message.Message,), dict(
DESCRIPTOR = _UPLOADOPERATION,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadOperation)
))
_sym_db.RegisterMessage(UploadOperation)
EnhancedChallenge = _reflection.GeneratedProtocolMessageType('EnhancedChallenge', (_message.Message,), dict(
DESCRIPTOR = _ENHANCEDCHALLENGE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.EnhancedChallenge)
))
_sym_db.RegisterMessage(EnhancedChallenge)
ClientFpChallenge = _reflection.GeneratedProtocolMessageType('ClientFpChallenge', (_message.Message,), dict(
DESCRIPTOR = _CLIENTFPCHALLENGE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.ClientFpChallenge)
))
_sym_db.RegisterMessage(ClientFpChallenge)
ChallengeInfo = _reflection.GeneratedProtocolMessageType('ChallengeInfo', (_message.Message,), dict(
DESCRIPTOR = _CHALLENGEINFO,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.ChallengeInfo)
))
_sym_db.RegisterMessage(ChallengeInfo)
EnhancedChallengeResponse = _reflection.GeneratedProtocolMessageType('EnhancedChallengeResponse', (_message.Message,), dict(
DESCRIPTOR = _ENHANCEDCHALLENGERESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.EnhancedChallengeResponse)
))
_sym_db.RegisterMessage(EnhancedChallengeResponse)
SignedChallengeInfo = _reflection.GeneratedProtocolMessageType('SignedChallengeInfo', (_message.Message,), dict(
DESCRIPTOR = _SIGNEDCHALLENGEINFO,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.SignedChallengeInfo)
))
_sym_db.RegisterMessage(SignedChallengeInfo)
TrackSample = _reflection.GeneratedProtocolMessageType('TrackSample', (_message.Message,), dict(
DESCRIPTOR = _TRACKSAMPLE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.TrackSample)
))
_sym_db.RegisterMessage(TrackSample)
UploadPlaylistRequest = _reflection.GeneratedProtocolMessageType('UploadPlaylistRequest', (_message.Message,), dict(
DESCRIPTOR = _UPLOADPLAYLISTREQUEST,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadPlaylistRequest)
))
_sym_db.RegisterMessage(UploadPlaylistRequest)
PlaylistResponse = _reflection.GeneratedProtocolMessageType('PlaylistResponse', (_message.Message,), dict(
DESCRIPTOR = _PLAYLISTRESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.PlaylistResponse)
))
_sym_db.RegisterMessage(PlaylistResponse)
UploadPlaylistResponse = _reflection.GeneratedProtocolMessageType('UploadPlaylistResponse', (_message.Message,), dict(
DESCRIPTOR = _UPLOADPLAYLISTRESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadPlaylistResponse)
))
_sym_db.RegisterMessage(UploadPlaylistResponse)
UploadPlaylistEntryRequest = _reflection.GeneratedProtocolMessageType('UploadPlaylistEntryRequest', (_message.Message,), dict(
DESCRIPTOR = _UPLOADPLAYLISTENTRYREQUEST,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadPlaylistEntryRequest)
))
_sym_db.RegisterMessage(UploadPlaylistEntryRequest)
PlaylistEntryResponse = _reflection.GeneratedProtocolMessageType('PlaylistEntryResponse', (_message.Message,), dict(
DESCRIPTOR = _PLAYLISTENTRYRESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.PlaylistEntryResponse)
))
_sym_db.RegisterMessage(PlaylistEntryResponse)
UploadPlaylistEntryResponse = _reflection.GeneratedProtocolMessageType('UploadPlaylistEntryResponse', (_message.Message,), dict(
DESCRIPTOR = _UPLOADPLAYLISTENTRYRESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadPlaylistEntryResponse)
))
_sym_db.RegisterMessage(UploadPlaylistEntryResponse)
UploadMetadataRequest = _reflection.GeneratedProtocolMessageType('UploadMetadataRequest', (_message.Message,), dict(
DESCRIPTOR = _UPLOADMETADATAREQUEST,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadMetadataRequest)
))
_sym_db.RegisterMessage(UploadMetadataRequest)
UpdateUploadStateRequest = _reflection.GeneratedProtocolMessageType('UpdateUploadStateRequest', (_message.Message,), dict(
DESCRIPTOR = _UPDATEUPLOADSTATEREQUEST,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UpdateUploadStateRequest)
))
_sym_db.RegisterMessage(UpdateUploadStateRequest)
ClientStateRequest = _reflection.GeneratedProtocolMessageType('ClientStateRequest', (_message.Message,), dict(
DESCRIPTOR = _CLIENTSTATEREQUEST,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.ClientStateRequest)
))
_sym_db.RegisterMessage(ClientStateRequest)
ClientStateResponse = _reflection.GeneratedProtocolMessageType('ClientStateResponse', (_message.Message,), dict(
DESCRIPTOR = _CLIENTSTATERESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.ClientStateResponse)
))
_sym_db.RegisterMessage(ClientStateResponse)
UploadMetadataResponse = _reflection.GeneratedProtocolMessageType('UploadMetadataResponse', (_message.Message,), dict(
DESCRIPTOR = _UPLOADMETADATARESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadMetadataResponse)
))
_sym_db.RegisterMessage(UploadMetadataResponse)
TrackSampleResponse = _reflection.GeneratedProtocolMessageType('TrackSampleResponse', (_message.Message,), dict(
DESCRIPTOR = _TRACKSAMPLERESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.TrackSampleResponse)
))
_sym_db.RegisterMessage(TrackSampleResponse)
UploadSampleRequest = _reflection.GeneratedProtocolMessageType('UploadSampleRequest', (_message.Message,), dict(
DESCRIPTOR = _UPLOADSAMPLEREQUEST,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadSampleRequest)
))
_sym_db.RegisterMessage(UploadSampleRequest)
UploadSampleResponse = _reflection.GeneratedProtocolMessageType('UploadSampleResponse', (_message.Message,), dict(
DESCRIPTOR = _UPLOADSAMPLERESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadSampleResponse)
))
_sym_db.RegisterMessage(UploadSampleResponse)
ImageUnion = _reflection.GeneratedProtocolMessageType('ImageUnion', (_message.Message,), dict(
DESCRIPTOR = _IMAGEUNION,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.ImageUnion)
))
_sym_db.RegisterMessage(ImageUnion)
UploadResponse = _reflection.GeneratedProtocolMessageType('UploadResponse', (_message.Message,), dict(
DESCRIPTOR = _UPLOADRESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadResponse)
))
_sym_db.RegisterMessage(UploadResponse)
TracksToUpload = _reflection.GeneratedProtocolMessageType('TracksToUpload', (_message.Message,), dict(
DESCRIPTOR = _TRACKSTOUPLOAD,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.TracksToUpload)
))
_sym_db.RegisterMessage(TracksToUpload)
GetJobsRequest = _reflection.GeneratedProtocolMessageType('GetJobsRequest', (_message.Message,), dict(
DESCRIPTOR = _GETJOBSREQUEST,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetJobsRequest)
))
_sym_db.RegisterMessage(GetJobsRequest)
GetJobsResponse = _reflection.GeneratedProtocolMessageType('GetJobsResponse', (_message.Message,), dict(
DESCRIPTOR = _GETJOBSRESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.GetJobsResponse)
))
_sym_db.RegisterMessage(GetJobsResponse)
UpAuthRequest = _reflection.GeneratedProtocolMessageType('UpAuthRequest', (_message.Message,), dict(
DESCRIPTOR = _UPAUTHREQUEST,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UpAuthRequest)
))
_sym_db.RegisterMessage(UpAuthRequest)
UpAuthResponse = _reflection.GeneratedProtocolMessageType('UpAuthResponse', (_message.Message,), dict(
DESCRIPTOR = _UPAUTHRESPONSE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UpAuthResponse)
))
_sym_db.RegisterMessage(UpAuthResponse)
DeleteUploadRequestedRequest = _reflection.GeneratedProtocolMessageType('DeleteUploadRequestedRequest', (_message.Message,), dict(
DESCRIPTOR = _DELETEUPLOADREQUESTEDREQUEST,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.DeleteUploadRequestedRequest)
))
_sym_db.RegisterMessage(DeleteUploadRequestedRequest)
ClientPolicy = _reflection.GeneratedProtocolMessageType('ClientPolicy', (_message.Message,), dict(
DESCRIPTOR = _CLIENTPOLICY,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.ClientPolicy)
))
_sym_db.RegisterMessage(ClientPolicy)
UploadMetadataInternalRequest = _reflection.GeneratedProtocolMessageType('UploadMetadataInternalRequest', (_message.Message,), dict(
DESCRIPTOR = _UPLOADMETADATAINTERNALREQUEST,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.UploadMetadataInternalRequest)
))
_sym_db.RegisterMessage(UploadMetadataInternalRequest)
OverrideConfigValue = _reflection.GeneratedProtocolMessageType('OverrideConfigValue', (_message.Message,), dict(
DESCRIPTOR = _OVERRIDECONFIGVALUE,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.OverrideConfigValue)
))
_sym_db.RegisterMessage(OverrideConfigValue)
OverrideConfigValueCollection = _reflection.GeneratedProtocolMessageType('OverrideConfigValueCollection', (_message.Message,), dict(
DESCRIPTOR = _OVERRIDECONFIGVALUECOLLECTION,
__module__ = 'upload_pb2'
# @@protoc_insertion_point(class_scope:wireless_android_skyjam.OverrideConfigValueCollection)
))
_sym_db.RegisterMessage(OverrideConfigValueCollection)
# @@protoc_insertion_point(module_scope)
gmusicapi-12.1.1/gmusicapi/protocol/webclient.py 0000664 0001750 0001750 00000041742 13374625453 022247 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
"""Calls made by the web client."""
from __future__ import print_function, division, absolute_import, unicode_literals
from six import raise_from
from builtins import * # noqa
import base64
import copy
import hmac
import random
import string
from hashlib import sha1
import validictory
import json
from gmusicapi.exceptions import CallFailure, ValidationException
from gmusicapi.protocol.shared import Call, authtypes
from gmusicapi.utils import utils, jsarray
base_url = 'https://play.google.com/music/'
service_url = base_url + 'services/'
class Init(Call):
"""Called one time per session, immediately after login.
This performs one-time setup:
it gathers the cookies we need (specifically `xt`), and Google uses it
to create the webclient DOM.
Note the use of the HEAD verb. Google uses GET, but we don't need
the large response containing Google's webui.
"""
static_method = 'HEAD'
static_url = base_url + 'listen'
required_auth = authtypes(sso=True)
# This call doesn't actually request/return anything useful aside from cookies.
@staticmethod
def parse_response(response):
return response.text
@classmethod
def check_success(cls, response, msg):
if response.status_code != 200:
raise CallFailure(('status code %s != 200' % response.status_code), cls.__name__)
if 'xt' not in response.cookies:
raise CallFailure('did not receieve xt cookies', cls.__name__)
class WcCall(Call):
"""Abstract base for web client calls."""
required_auth = authtypes(xt=True, sso=True)
# validictory schema for the response
_res_schema = utils.NotImplementedField
@classmethod
def validate(cls, response, msg):
"""Use validictory and a static schema (stored in cls._res_schema)."""
try:
return validictory.validate(msg, cls._res_schema)
except ValueError as e:
raise_from(ValidationException(str(e)), e)
@classmethod
def check_success(cls, response, msg):
# Failed responses always have a success=False key.
# Some successful responses do not have a success=True key, however.
# TODO remove utils.call_succeeded
if 'success' in msg and not msg['success']:
raise CallFailure(
"the server reported failure. This is usually"
" caused by bad arguments, but can also happen if requests"
" are made too quickly (eg creating a playlist then"
" modifying it before the server has created it)",
cls.__name__)
@classmethod
def parse_response(cls, response):
return cls._parse_json(response.text)
class CreatePlaylist(WcCall):
"""Adds songs to a playlist."""
static_method = 'POST'
static_url = service_url + 'createplaylist'
static_params = {'format': 'jsarray'}
_res_schema = {
"type": "array",
# eg:
# [[0,2]
# ,["id","sharetoken",[]
# ,]]
}
@staticmethod
def dynamic_data(name, description, public, session_id=""):
return json.dumps([[session_id, 1], [public, name, description, []]])
class AddToPlaylist(WcCall):
"""Adds songs to a playlist."""
static_method = 'POST'
static_url = service_url + 'addtoplaylist'
_res_schema = {
"type": "object",
"properties": {
"playlistId": {"type": "string"},
"songIds": {
"type": "array",
"items": {
"type": "object",
"properties": {
"songId": {"type": "string"},
"playlistEntryId": {"type": "string"}
}
}
}
},
"additionalProperties": False
}
@staticmethod
def dynamic_data(playlist_id, song_ids):
"""
:param playlist_id: id of the playlist to add to.
:param song_ids: a list of song ids
"""
# TODO unsure what type means here. Likely involves uploaded vs store/free.
song_refs = [{'id': sid, 'type': 1} for sid in song_ids]
return {
'json': json.dumps(
{"playlistId": playlist_id, "songRefs": song_refs}
)
}
@staticmethod
def filter_response(msg):
filtered = copy.copy(msg)
filtered['songIds'] = ["<%s songs>" % len(filtered.get('songIds', []))]
return filtered
class ChangePlaylistOrder(WcCall):
"""Reorder existing tracks in a playlist."""
static_method = 'POST'
static_url = service_url + 'changeplaylistorder'
_res_schema = {
"type": "object",
"properties": {
"afterEntryId": {"type": "string", "blank": True},
"playlistId": {"type": "string"},
"movedSongIds": {
"type": "array",
"items": {"type": "string"}
}
},
"additionalProperties": False
}
@staticmethod
def dynamic_data(playlist_id, song_ids_moving, entry_ids_moving,
after_entry_id=None, before_entry_id=None):
"""
:param playlist_id: id of the playlist getting reordered.
:param song_ids_moving: a list of consecutive song ids. Matches entry_ids_moving.
:param entry_ids_moving: a list of consecutive entry ids to move. Matches song_ids_moving.
:param after_entry_id: the entry id to place these songs after. Default first position.
:param before_entry_id: the entry id to place these songs before. Default last position.
"""
# empty string means first/last position
if after_entry_id is None:
after_entry_id = ""
if before_entry_id is None:
before_entry_id = ""
return {
'json': json.dumps(
{
"playlistId": playlist_id,
"movedSongIds": song_ids_moving,
"movedEntryIds": entry_ids_moving,
"afterEntryId": after_entry_id,
"beforeEntryId": before_entry_id
}
)
}
@staticmethod
def filter_response(msg):
filtered = copy.copy(msg)
filtered['movedSongIds'] = ["<%s songs>" % len(filtered.get('movedSongIds', []))]
return filtered
class DeletePlaylist(WcCall):
"""Delete a playlist."""
static_method = 'POST'
static_url = service_url + 'deleteplaylist'
_res_schema = {
"type": "object",
"properties": {
"deleteId": {"type": "string"}
},
"additionalProperties": False
}
@staticmethod
def dynamic_data(playlist_id):
"""
:param playlist_id: id of the playlist to delete.
"""
return {
'json': json.dumps(
{"id": playlist_id}
)
}
class DeleteSongs(WcCall):
"""Delete a song from the entire library or a single playlist."""
static_method = 'POST'
static_url = service_url + 'deletesong'
_res_schema = {
"type": "object",
"properties": {
"listId": {"type": "string"},
"deleteIds":
{
"type": "array",
"items": {"type": "string"}
}
},
"additionalProperties": False
}
@staticmethod
def dynamic_data(song_ids, playlist_id='all', entry_ids=None):
"""
:param song_ids: a list of song ids.
:param playlist_id: playlist id to delete from, or 'all' for deleting from library.
:param entry_ids: when deleting from playlists, corresponding list of entry ids.
"""
if entry_ids is None:
# this is strange, but apparently correct
entry_ids = [''] * len(song_ids)
return {
'json': json.dumps(
{"songIds": song_ids, "entryIds": entry_ids, "listId": playlist_id}
)
}
@staticmethod
def filter_response(msg):
filtered = copy.copy(msg)
filtered['deleteIds'] = ["<%s songs>" % len(filtered.get('deleteIds', []))]
return filtered
class ChangeSongMetadata(WcCall):
"""Edit the metadata of songs."""
static_method = 'POST'
static_url = service_url + 'modifytracks'
static_params = {'format': 'jsarray'}
_res_schema = {
"type": "array",
# eg [[0,1],[1393706382978]]
}
@staticmethod
def dynamic_data(songs, session_id=""):
"""
:param songs: a list of dicts ``{'id': '...', 'albumArtUrl': '...'}``
"""
supported = {'id', 'albumArtUrl', 'title', 'artist', 'albumArtist', 'album'}
for s in songs:
for k in s.keys():
if k not in supported:
raise ValueError("ChangeSongMetadata only supports the the following keys: "
+ str(supported) +
". All other keys must be removed. Key encountered:" + k)
# jsarray is just wonderful
jsarray = [[session_id, 1]]
song_arrays = [[s['id'],
s.get('title'),
s.get('albumArtUrl'),
s.get('artist'),
s.get('album'),
s.get('albumArtist')]
+ [None] * 33 + [[]] for s in songs]
jsarray.append([song_arrays])
return json.dumps(jsarray)
class GetDownloadInfo(WcCall):
"""Get download links and counts for songs."""
static_method = 'POST'
static_url = service_url + 'multidownload'
_res_schema = {
"type": "object",
"properties": {
"downloadCounts": {
"type": "object",
"items": {
"type": "object",
"properties": {
"id": {"type": "integer"}
}
}
},
"url": {"type": "string"}
},
"additionalProperties": False
}
@staticmethod
def dynamic_data(song_ids):
"""
:param: (list) song_ids
"""
return {'json': json.dumps({'songIds': song_ids})}
class GetStreamUrl(WcCall):
"""Used to request a streaming link of a track."""
static_method = 'GET'
static_url = base_url + 'play' # note use of base_url, not service_url
required_auth = authtypes(sso=True) # no xt required
_res_schema = {
"type": "object",
"properties": {
"url": {"type": "string", "required": False},
"urls": {"type": "array", "required": False},
'now': {'type': 'integer', 'required': False},
'tier': {'type': 'integer', 'required': False},
'replayGain': {'type': 'integer'},
'streamAuthId': {'type': 'string'},
'isFreeRadioUser': {'type': 'boolean'},
},
"additionalProperties": False
}
@staticmethod
def dynamic_params(song_id):
# https://github.com/simon-weber/gmusicapi/issues/137
# there are three cases when streaming:
# | track type | guid songid? | slt/sig needed? |
# user-uploaded yes no
# AA track in library yes yes
# AA track not in library no yes
# without the track['type'] field we can't tell between 1 and 2, but
# include slt/sig anyway; the server ignores the extra params.
key = '27f7313e-f75d-445a-ac99-56386a5fe879'.encode("ascii")
salt = ''.join(random.choice(string.ascii_lowercase + string.digits) for x in range(12))
salted_id = (song_id + salt).encode("utf-8")
sig = base64.urlsafe_b64encode(hmac.new(key, salted_id, sha1).digest())[:-1]
params = {
'u': 0,
'pt': 'e',
'slt': salt,
'sig': sig
}
# TODO match guid instead, should be more robust
if song_id[0] == 'T':
# all access
params['mjck'] = song_id
else:
params['songid'] = song_id
return params
class ReportBadSongMatch(WcCall):
"""Request to signal the uploader to reupload a matched track."""
static_method = 'POST'
static_url = service_url + 'fixsongmatch'
static_params = {'format': 'jsarray'}
# This no longer holds.
expected_response = [[0], []]
@classmethod
def validate(cls, response, msg):
pass
# if msg != cls.expected_response:
# raise ValidationException("response != %r" % cls.expected_response)
@staticmethod
def dynamic_data(song_ids):
return json.dumps([["", 1], [song_ids]])
class UploadImage(WcCall):
"""Upload an image for use as album art."""
static_method = 'POST'
static_url = service_url + 'imageupload'
static_params = {'zx': '', # ??
'u': 0}
_res_schema = {
'type': 'object',
'properties': {
'imageUrl': {'type': 'string', 'blank': False},
'imageDisplayUrl': {'type': 'string', 'blank': False},
},
'additionalProperties': False
}
@staticmethod
def dynamic_files(image_filepath):
"""
:param image_filepath: path to an image
"""
with open(image_filepath, 'rb') as f:
contents = f.read()
return {'albumArt': (image_filepath, contents)}
class GetSettings(WcCall):
"""Get data that populates the settings tab: labs and devices."""
static_method = 'POST'
static_url = service_url + 'fetchsettings'
_device_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'deviceType': {'type': 'integer'},
'id': {'type': 'string'},
'lastAccessedFormatted': {'type': 'string'},
'lastAccessedTimeMillis': {'type': 'integer'},
'lastEventTimeMillis': {'type': 'integer'},
'name': {'type': 'string', 'blank': True},
# only for type == 2 (android phone?):
'model': {'type': 'string', 'blank': True, 'required': False},
'manufacturer': {'type': 'string', 'blank': True, 'required': False},
'carrier': {'type': 'string', 'blank': True, 'required': False},
},
}
_res_schema = {
'type': 'object',
'additionalProperties': False,
'properties': {
'settings': {
'type': 'object',
'additionalProperties': False,
'properties': {
'entitlementInfo': {
'type': 'object',
'additionalProperties': False,
'properties': {
'expirationMillis': {'type': 'integer', 'required': False},
'isCanceled': {'type': 'boolean'},
'isSubscription': {'type': 'boolean'},
'isTrial': {'type': 'boolean'},
}},
'lab': {
'type': 'array',
'items': {
'type': 'object',
'additionalProperties': False,
'properties': {
'description': {'type': 'string'},
'enabled': {'type': 'boolean'},
'displayName': {'type': 'string'},
'experimentName': {'type': 'string'},
},
}},
'maxUploadedTracks': {'type': 'integer'},
'subscriptionNewsletter': {'type': 'boolean', 'required': False},
'uploadDevice': {
'type': 'array',
'items': _device_schema,
}},
}
},
}
@staticmethod
def dynamic_data(session_id):
"""
:param: session_id
"""
return {'json': json.dumps({'sessionId': session_id})}
class DeauthDevice(WcCall):
"""Deauthorize a device from GetSettings."""
static_method = 'POST'
static_url = service_url + 'modifysettings'
@staticmethod
def dynamic_data(device_id, session_id):
return {'json': json.dumps({'deauth': device_id, 'sessionId': session_id})}
@classmethod
def validate(cls, response, msg):
if msg.text != '{}':
raise ValidationException("expected an empty object; received %r" % msg.text)
class GetSharedPlaylist(WcCall):
"""Get the contents and metadata for a shared playlist."""
static_method = 'POST'
static_url = service_url + 'loadsharedplaylist'
static_params = {'format': 'jsarray'}
_res_schema = {
'type': 'array',
}
@classmethod
def parse_response(cls, response):
return cls._parse_json(jsarray.to_json(response.text))
@staticmethod
def dynamic_data(session_id, share_token):
return json.dumps([
[session_id, 1],
[share_token]
])
gmusicapi-12.1.1/gmusicapi/session.py 0000664 0001750 0001750 00000025235 13400371522 020075 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
"""
Sessions handle the details of authentication and transporting requests.
"""
from __future__ import print_function, division, absolute_import, unicode_literals
from builtins import * # noqa
from collections import namedtuple
from contextlib import closing
import json
import gpsoauth
import httplib2 # included with oauth2client
import mechanicalsoup
import oauth2client
from oauth2client.client import OAuth2Credentials
import requests
from gmusicapi.exceptions import (
AlreadyLoggedIn, NotLoggedIn, CallFailure
)
from gmusicapi.protocol import webclient
from gmusicapi.utils import utils
log = utils.DynamicClientLogger(__name__)
OAuthInfo = namedtuple('OAuthInfo', 'client_id client_secret scope redirect_uri')
def credentials_from_refresh_token(token, oauth_info):
# why doesn't Google provide this!?
cred_json = {"_module": "oauth2client.client",
"token_expiry": "2000-01-01T00:13:37Z", # to refresh now
"access_token": 'bogus',
"token_uri": "https://accounts.google.com/o/oauth2/token",
"invalid": False,
"token_response": {
"access_token": 'bogus',
"token_type": "Bearer",
"expires_in": 3600,
"refresh_token": token},
"client_id": oauth_info.client_id,
"id_token": None,
"client_secret": oauth_info.client_secret,
"revoke_uri": "https://accounts.google.com/o/oauth2/revoke",
"_class": "OAuth2Credentials",
"refresh_token": token,
"user_agent": None}
return OAuth2Credentials.new_from_json(json.dumps(cred_json))
class _Base(object):
def __init__(self, rsession_setup=None):
"""
:param rsession_setup: a callable that will be called with
the backing requests.Session to delegate config to callers.
"""
self._rsession = requests.Session()
if rsession_setup is None:
rsession_setup = lambda x: x # noqa
self._rsession_setup = rsession_setup
self._rsession_setup(self._rsession)
self.is_authenticated = False
def _send_with_auth(self, req_kwargs, desired_auth, rsession):
raise NotImplementedError
def _send_without_auth(self, req_kwargs, rsession):
return rsession.request(**req_kwargs)
def login(self, *args, **kwargs):
# subclasses extend / use super()
if self.is_authenticated:
raise AlreadyLoggedIn
def logout(self):
"""
Reset the session to an unauthenticated, default state.
"""
self._rsession.close()
self._rsession = requests.Session()
self._rsession_setup(self._rsession)
self.is_authenticated = False
def send(self, req_kwargs, desired_auth, rsession=None):
"""Send a request from a Call using this session's auth.
:param req_kwargs: kwargs for requests.Session.request
:param desired_auth: protocol.shared.AuthTypes to attach
:param rsession: (optional) a requests.Session to use
(default ``self._rsession`` - this is exposed for test purposes)
"""
res = None
if not any(desired_auth):
if rsession is None:
# use a throwaway session to ensure it's clean
with closing(requests.Session()) as new_session:
self._rsession_setup(new_session)
res = self._send_without_auth(req_kwargs, new_session)
else:
res = self._send_without_auth(req_kwargs, rsession)
else:
if not self.is_authenticated:
raise NotLoggedIn
if rsession is None:
rsession = self._rsession
res = self._send_with_auth(req_kwargs, desired_auth, rsession)
return res
class Webclient(_Base):
def login(self, email, password, *args, **kwargs):
"""
Perform serviceloginauth then retrieve webclient cookies.
:param email:
:param password:
"""
super(Webclient, self).login()
# Google's login form has a bunch of hidden fields I'd rather not deal with manually.
browser = mechanicalsoup.Browser(soup_config={"features": "html.parser"})
login_page = browser.get('https://accounts.google.com/ServiceLoginAuth',
params={'service': 'sj',
'continue': 'https://play.google.com/music/listen'})
form_candidates = login_page.soup.select("form")
if len(form_candidates) > 1:
log.error("Google login form dom has changed; there are %s candidate forms:\n%s",
len(form_candidates), form_candidates)
return False
form = form_candidates[0]
form.select("#Email")[0]['value'] = email
response = browser.submit(form, 'https://accounts.google.com/AccountLoginInfo')
try:
response.raise_for_status()
except requests.HTTPError:
log.exception("submitting login form failed")
return False
form_candidates = response.soup.select("form")
if len(form_candidates) > 1:
log.error("Google login form dom has changed; there are %s candidate forms:\n%s",
len(form_candidates), form_candidates)
return False
form = form_candidates[0]
form.select("#Passwd")[0]['value'] = password
response = browser.submit(form, 'https://accounts.google.com/ServiceLoginAuth')
try:
response.raise_for_status()
except requests.HTTPError:
log.exception("submitting login form failed")
return False
# We can't use in without .keys(), since international users will see a
# CookieConflictError.
if 'SID' not in list(browser.session.cookies.keys()):
# Invalid auth.
return False
self._rsession.cookies.update(browser.session.cookies)
self.is_authenticated = True
# Get webclient cookies.
# They're stored automatically by requests on the webclient session.
try:
webclient.Init.perform(self, True)
except CallFailure:
log.exception("unable to initialize webclient cookies")
self.logout()
return self.is_authenticated
def _send_with_auth(self, req_kwargs, desired_auth, rsession):
if desired_auth.xt:
req_kwargs.setdefault('params', {})
req_kwargs['params'].update({'u': 0, 'xt': rsession.cookies['xt']})
return rsession.request(**req_kwargs)
class Musicmanager(_Base):
oauth = OAuthInfo(
'652850857958.apps.googleusercontent.com',
'ji1rklciNp2bfsFJnEH_i6al',
'https://www.googleapis.com/auth/musicmanager',
'urn:ietf:wg:oauth:2.0:oob'
)
def __init__(self, *args, **kwargs):
super(Musicmanager, self).__init__(*args, **kwargs)
self._oauth_creds = None
def login(self, oauth_credentials, *args, **kwargs):
"""Store an already-acquired oauth2client.Credentials."""
super(Musicmanager, self).login()
try:
# refresh the token right away to check auth validity
oauth_credentials.refresh(httplib2.Http())
except oauth2client.client.Error:
log.exception("error when refreshing oauth credentials")
if oauth_credentials.access_token_expired:
log.info("could not refresh oauth credentials")
return False
self._oauth_creds = oauth_credentials
self.is_authenticated = True
return self.is_authenticated
def _send_with_auth(self, req_kwargs, desired_auth, rsession):
if desired_auth.oauth:
if self._oauth_creds.access_token_expired:
self._oauth_creds.refresh(httplib2.Http())
req_kwargs['headers'] = req_kwargs.get('headers', {})
req_kwargs['headers']['Authorization'] = \
'Bearer ' + self._oauth_creds.access_token
return rsession.request(**req_kwargs)
class Mobileclient(Musicmanager):
oauth = OAuthInfo(
'228293309116.apps.googleusercontent.com',
'GL1YV0XMp0RlL7ylCV3ilFz-',
'https://www.googleapis.com/auth/skyjam',
'urn:ietf:wg:oauth:2.0:oob'
)
def __init__(self, *args, **kwargs):
super(Mobileclient, self).__init__(*args, **kwargs)
self._master_token = None
self._authtoken = None
self._locale = None
self._is_subscribed = None
def gpsoauth_login(self, email, password, android_id, *args, **kwargs):
"""
Get a master token, then use it to get a skyjam OAuth token.
:param email:
:param password:
:param android_id:
"""
# TODO calling directly into base is weird
_Base.login(self, email, password, android_id, *args, **kwargs)
res = gpsoauth.perform_master_login(email, password, android_id)
if 'Token' not in res:
return False
self._master_token = res['Token']
res = gpsoauth.perform_oauth(
email, self._master_token, android_id,
service='sj', app='com.google.android.music',
client_sig='38918a453d07199354f8b19af05ec6562ced5788')
if 'Auth' not in res:
return False
self._authtoken = res['Auth']
self.is_authenticated = True
return True
def _send_with_auth(self, req_kwargs, desired_auth, rsession):
# Default to English (United States) if no locale given.
if not self._locale:
self._locale = 'en_US'
# Set locale for all Mobileclient calls.
req_kwargs.setdefault('params', {})
req_kwargs['params'].update({'hl': self._locale})
# As of API v2.5, dv is a required parameter for all calls.
# The dv value is part of the Android app version number,
# but setting this to 0 works fine.
req_kwargs['params'].update({'dv': 0})
if self._is_subscribed:
req_kwargs['params'].update({'tier': 'aa'})
else:
req_kwargs['params'].update({'tier': 'fr'})
req_kwargs.setdefault('headers', {})
if desired_auth.gpsoauth:
# does this expire?
req_kwargs['headers']['Authorization'] = \
'GoogleLogin auth=' + self._authtoken
return rsession.request(**req_kwargs)
if desired_auth.oauth:
return super(Mobileclient, self)._send_with_auth(req_kwargs, desired_auth, rsession)
raise ValueError("_send_with_auth got invalid desired_auth: {}".format(desired_auth))
gmusicapi-12.1.1/gmusicapi/test/ 0000755 0001750 0001750 00000000000 13512455420 017012 5 ustar simon simon 0000000 0000000 gmusicapi-12.1.1/gmusicapi/test/__init__.py 0000600 0001750 0001750 00000000030 12667715127 021121 0 ustar simon simon 0000000 0000000 # -*- coding: utf-8 -*-
gmusicapi-12.1.1/gmusicapi/test/audiotest_small.mp3 0000600 0001750 0001750 00000004071 12667715127 022633 0 ustar simon simon 0000000 0000000 ID3 TIT2 quick.mp3 TPE1 gmusicapi test artist TALB album TSSE
Lavf53.21.1 ÿóÄ Xø Gÿœÿ9»å%
EÒåÿóÄÀ< @óÿ«õq>'ð ÿóÄ(j¼Š ú~#Ê÷§÷EG±ÿóÄð –·-xDì8éOòê,@ÿóĨAË mLþÿE