def dynamic_data(cls, playlist_id): """ :param playlist_id: id to retrieve from, or 'all' to get all playlists. """ # This call has a dynamic response schema based on the request. # TODO wow, this is a terrible idea if playlist_id == "all": cls._res_schema = {"type": "object", "properties": {"playlists": pl_array}, "additionalProperties": False} return {"json": json.dumps({})} else: cls._res_schema = pl_schema return {"json": json.dumps({"id": playlist_id})}
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 } ) }
def dynamic_data(cont_token=None): """:param cont_token: (optional) token to get the next library chunk.""" if not cont_token: req = {} else: req = {"continuationToken": cont_token} return {'json': json.dumps(req)}
def dynamic_data(sid, plays, playtime): #TODO this can support multiple return json.dumps({'track_stats': [{ 'id': sid, 'incremental_plays': plays, 'last_play_time_millis': str(utils.datetime_to_microseconds(playtime)), 'type': 2 if sid.startswith('T') else 1, 'track_events': [], }]})
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})}
def dynamic_data(playlist_id): """ :param playlist_id: id of the playlist to delete. """ return { 'json': json.dumps( {"id": playlist_id} ) }
def dynamic_data(playlist_id, new_name): """ :param playlist_id: id of the playlist to rename. :param new_title: desired title. """ return { 'json': json.dumps( {"playlistId": playlist_id, "playlistName": new_name} ) }
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})}
def dynamic_data(cls, playlist_id): """ :param playlist_id: id to retrieve from, or 'all' to get all playlists. """ #This call has a dynamic response schema based on the request. if playlist_id == 'all': cls._res_schema = { "type": "object", "properties": { "playlists": pl_array, }, "additionalProperties": False } return {'json': json.dumps({})} else: cls._res_schema = pl_schema return {'json': json.dumps({'id': playlist_id})}
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. 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, basestring): payload = str(payload) message['createSessionRequest']['fields'].append( { "inlined": { "content": payload, "name": key } } ) return json.dumps(message)
def dynamic_data(songs, session_id=""): """ :param songs: a list of dicts ``{'id': '...', 'albumArtUrl': '...'}`` """ if any([s for s in songs if set(s.keys()) != set(['id', 'albumArtUrl'])]): raise ValueError("ChangeSongMetadata only supports the 'id' and 'albumArtUrl' keys." " All other keys must be removed.") # jsarray is just wonderful jsarray = [[session_id, 1]] song_arrays = [[s['id'], None, s['albumArtUrl']] + [None] * 36 + [[]] for s in songs] jsarray.append([song_arrays]) return json.dumps(jsarray)
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)
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 return json.dumps( { "contentFilter": 1, "stations": [{"numEntries": num_entries, "radioId": station_id, "recentlyPlayed": recently_played}], } )
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 }) }
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, }]})
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 return json.dumps({'contentFilter': 1, 'stations': [ { 'numEntries': num_entries, 'radioId': station_id, 'recentlyPlayed': recently_played } ]})
def credentials_from_refresh_token(token): # 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.client_id, "id_token": None, "client_secret": oauth.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))
def credentials_from_refresh_token(token): # 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.client_id, "id_token": None, "client_secret": oauth.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))
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]})
def dynamic_data(songs): """ :param songs: a list of dictionary representations of songs """ return {"json": json.dumps({"entries": songs})}
def dynamic_data(query): return {'json': json.dumps({'q': query})}
def dynamic_data(session_id): """ :param: session_id """ return {'json': json.dumps({'sessionId': session_id})}
def dynamic_data(device_id, session_id): return {"json": json.dumps({"deauth": device_id, "sessionId": session_id})}
def dynamic_data(name, description, public, session_id=""): return json.dumps([[session_id, 1], [public, name, description, []]])
def dynamic_data(song_ids): """ :param: (list) song_ids """ return {'json': json.dumps({'songIds': song_ids})}
def dynamic_data(device_id, session_id): return {'json': json.dumps({'deauth': device_id, 'sessionId': session_id})}
def dynamic_data(session_id): """ :param: session_id """ return {"json": json.dumps({"sessionId": session_id})}
def dynamic_data(title): """ :param title: the title of the playlist to create. """ return {'json': json.dumps({"title": title})}
def dynamic_data(mutations): """ :param mutations: list of mutation dictionaries """ return json.dumps({'mutations': mutations})
def dynamic_data(songs): """ :param songs: a list of dictionary representations of songs """ return {'json': json.dumps({'entries': songs})}
def dynamic_data(session_id, share_token): return json.dumps([ [session_id, 1], [share_token] ])
def dynamic_data(song_ids): return json.dumps([["", 1], [song_ids]])
def dynamic_data(song_ids): """ :param: (list) song_ids """ return {"json": json.dumps({"songIds": song_ids})}
def dynamic_data(playlist_id): """ :param playlist_id: id of the playlist to delete. """ return {'json': json.dumps({"id": playlist_id})}
def dynamic_data(cls): tz_offset = calendar.timegm(time.localtime()) - calendar.timegm(time.gmtime()) return json.dumps({ 'requestSignals': {'timeZoneOffsetSecs': tz_offset} })
def dynamic_data(session_id, share_token): return json.dumps([[session_id, 1], [share_token]])