class GMusicWS: def __init__(self, user, password, playlistName): self.playlistName = playlistName self.api = Mobileclient() print("Logging into MobileClient API") self.api.login(user, password, "android_id") #insert unique android_id here def mapUnknownTracks(self, db): playlist = db.unmappedTracks() for track in playlist: searchstr = track.artist + " " + track.song print("Searching for %s" % (searchstr)) try: result = self.api.search_all_access(searchstr, max_results=1) print("Found " + result['song_hits'][0]['track']['artist'] + " - " + result['song_hits'][0]['track']['title']) nid = result['song_hits'][0]['track']['nid'] db.storemapping(track.song, track.artist, nid) except: print("Error parsing result: " + str(result)) time.sleep(1) def maintain(self, tracks): print("Searching for playlist %s" % (self.playlistName)) found = False searchres = self.api.get_all_playlists() for list in searchres: if list['name'] == self.playlistName: found = True pid = list['id'] if not found: print("Not found - creating") pid = self.api.create_playlist(self.playlistName) print("Playlist id is %s" % (pid)) print("Getting current contents") playlists = self.api.get_all_user_playlist_contents() currentEntries = [] for playlist in playlists: if playlist['name'] == self.playlistName: for entry in playlist['tracks']: currentEntries.append(entry['id']) print("Removing songs") self.api.remove_entries_from_playlist(currentEntries) print("Adding songs") self.api.add_songs_to_playlist(pid, tracks)
class GMusicWS: def __init__(self, user, password, playlistName): self.playlistName = playlistName self.api = Mobileclient() print ("Logging into MobileClient API") self.api.login(user, password,"android_id") #insert unique android_id here def mapUnknownTracks(self, db): playlist = db.unmappedTracks() for track in playlist: searchstr = track.artist + " " + track.song print ("Searching for %s" % (searchstr)) try: result = self.api.search_all_access(searchstr, max_results=1) print ("Found " + result['song_hits'][0]['track']['artist'] + " - " + result['song_hits'][0]['track']['title']) nid = result['song_hits'][0]['track']['nid'] db.storemapping(track.song, track.artist, nid) except: print ("Error parsing result: " + str(result)) time.sleep(1) def maintain(self, tracks): print ("Searching for playlist %s" % (self.playlistName)) found = False searchres = self.api.get_all_playlists() for list in searchres: if list['name'] == self.playlistName: found = True pid = list['id'] if not found: print ("Not found - creating") pid = self.api.create_playlist(self.playlistName) print ("Playlist id is %s" % (pid)) print ("Getting current contents") playlists = self.api.get_all_user_playlist_contents() currentEntries = [] for playlist in playlists: if playlist['name'] == self.playlistName: for entry in playlist['tracks']: currentEntries.append(entry['id']) print ("Removing songs") self.api.remove_entries_from_playlist(currentEntries) print ("Adding songs") self.api.add_songs_to_playlist(pid, tracks)
class PlayMusic: def __init__(self, google_username, google_password): self.client = Mobileclient(validate=False) # generate a stable, unique android id h = hashlib.sha256() h.update(google_username) android_id = h.hexdigest()[:16] self.client.login(google_username, google_password, android_id) def is_authenticated(self): return self.client.is_authenticated() @staticmethod def track_url(play_track): return "https://play.google.com/music/m/%s" % play_track["nid"] @staticmethod def playlist_url(playlist_id): return "https://play.google.com/music/playlist/%s" % playlist_id def search_tracks(self, query): if len(query) >= MAX_QUERY_LENGTH: # long queries don't work for some reason # for example "The Moderately Talented Yet Attractive Young Woman vs. The Exceptionally Talented Yet Not # So Attractive Middle Aged Man / Sun Kil Moon / Among The Leaves" parts = query.split(" ") query = parts.pop(0) for part in parts: if len(query) + 1 + len(part) > MAX_QUERY_LENGTH: break query += " " + part retries = RETRIES response = None while retries and not response: retries -= 1 try: response = self.client.search_all_access(query) except Exception, e: if not retries: raise e # sleep for two seconds before retrying time.sleep(2) return [PlayTrack(song["track"]) for song in response["song_hits"]]
class PlayMusic(): def __init__(self, google_username, google_password): self.client = Mobileclient(validate=False) # generate a stable, unique android id h = hashlib.sha256() h.update(google_username) android_id = h.hexdigest()[:16] self.client.login(google_username, google_password, android_id) def is_authenticated(self): return self.client.is_authenticated() @staticmethod def track_url(play_track): return 'https://play.google.com/music/m/%s' % play_track['nid'] @staticmethod def playlist_url(playlist_id): return 'https://play.google.com/music/playlist/%s' % playlist_id def search_tracks(self, query): if len(query) >= MAX_QUERY_LENGTH: # long queries don't work for some reason # for example "The Moderately Talented Yet Attractive Young Woman vs. The Exceptionally Talented Yet Not # So Attractive Middle Aged Man / Sun Kil Moon / Among The Leaves" parts = query.split(' ') query = parts.pop(0) for part in parts: if len(query) + 1 + len(part) > MAX_QUERY_LENGTH: break query += ' ' + part retries = RETRIES response = None while retries and not response: retries -= 1 try: response = self.client.search_all_access(query) except Exception, e: if not retries: raise e # sleep for two seconds before retrying time.sleep(2) return [PlayTrack(song['track']) for song in response['song_hits']]
#!/bin/python2 import sys from gmusicapi import Mobileclient if len(sys.argv) < 2: print "Please provide a query string" sys.exit(1) song_name = sys.argv[1] api = Mobileclient() is_logged_in = api.login('ztaticnull', 'kkjyumnguxiqiixx','0000000000000000') if not is_logged_in: print "Could not authenticate" sys.exit(1) results = api.search_all_access(song_name, max_results=100) songs = [(s['track']['storeId'], s['track']['artist'], s['track']['title']) for s in results['song_hits']] for s in songs: s = [x.encode('utf-8') for x in s] print "%s:%s - %s" % tuple(s)
class GooglePlayMusic(BaseModule): def __init__(self, client, options): super(GooglePlayMusic, self).__init__(client, options) self.api = Mobileclient() if self.api.login(self.options["username"], self.options["password"]): print "Login Successful" self.webClient = Webclient() self.webClient.login(self.options["username"], self.options["password"]) #self.findRegisteredDevice() self.deviceId = self.options["deviceId"] self.speaker = self.options["speaker"] self.playing_track_list = False self.current_track_list = [] self.current_track_index = 0 """ def findRegisteredDevice(self): webClient = Webclient() webClient.login(self.options["username"], self.options["password"]) registered_devices = webClient.get_registered_devices() for device in registered_devices: if device["type"] == "PHONE": self.deviceId = device["id"][2:] #removes the 0x from the front """ def onEvent(self, message): if message["from"] == self.speaker: if message["event"] == "streamEnded": if self.playing_track_list: self.continue_track_list() def onAction(self, message): if message["action"] == "search_and_play": query = message["data"]["query"] self.search_and_play(query) if message["action"] == "stopMusic" or message["action"] == "stop": self.stop_music() if message["action"] == "startRadio": query = message["data"]["query"] self.start_radio(query) def search_and_play(self, query): results = self.api.search_all_access(query) if not len(results["song_hits"]) > 0: return song_data = results["song_hits"][0]["track"] print song_data self.play_track(song_data) def play_track(self, song_data): print "Playing %s, by %s" % (song_data["title"], song_data["artist"]) stream_url = self.api.get_stream_url(song_data["nid"], self.deviceId) self.client.emitAction(self.speaker, "streamMP3", {"url": stream_url}) def stop_music(self): self.playing_track_list = False self.client.emitAction(self.speaker, "stopStreaming", {}) def start_radio(self, query): results = self.api.search_all_access(query, max_results=10) top_song = results["song_hits"][0] if len(results["song_hits"]) else None top_album = results["album_hits"][0] if len(results["album_hits"]) else None top_artist = results["artist_hits"][0] if len(results["artist_hits"]) else None if not top_song: top_song = {"score": 0} if not top_album: top_album = {"score": 0} if not top_artist: top_artist = {"score": 0} station_id = None if top_song["score"] > top_album["score"]and top_song["score"] > top_artist["score"]: station_id = self.api.create_station(query, track_id=top_song["track"]["nid"]) elif top_album["score"] > top_song["score"] and top_album["score"] > top_artist["score"]: station_id = self.api.create_station(query, album_id=top_album["album"]["albumId"]) else: station_id = self.api.create_station(query, artist_id=top_artist["artist"]["artistId"]) tracks = self.api.get_station_tracks(station_id) self.play_track_list(tracks) def play_track_list(self, tracks): self.playing_track_list = True self.current_track_list = tracks self.current_track_index = 0 self.play_track(self.current_track_list[0]) def continue_track_list(self): self.current_track_index += 1 self.play_track(self.current_track_list[self.current_track_index])
if logged_in == True: print ("Succesfully logged into Google!") ## Take a file with song names - artists and Playlists names beginning with P: all_playlists = parse_file("playlists.txt") missing_songs = [] for i in all_playlists: for j in i: if j[0:2] == "P:": current_playlist_name = j[3:] print ("Creating playlist: " + j[3:]) # current_playlist = api.create_playlist(j[3:]) #COMMENT OUT THIS LINE TO PREVENT PLAYLIST CREATION else: current_song = j[0][0] + " " + j[0][1] search_current_song = api.search_all_access(current_song, 1) if search_current_song["song_hits"] == []: print ("No results found for " + current_song) missing_songs.append([current_song, current_playlist_name]) continue current_song_id = search_current_song["song_hits"][0]["track"]["storeId"] print ("Adding " + current_song + " to playlist " + current_playlist_name) # api.add_songs_to_playlist(current_playlist, current_song_id) #COMMENT OUT THIS LINE TO PREVENT PLAYLIST CREATION for i in missing_songs: print ("Could not find " + i[0] + " for playlist " + i[1]) ##desperado = api.search_all_access("Desperado - The Eagles", 1) ##print(desperado['song_hits']) ##for i in desperado['song_hits']: ##song_ids = [i['track']['storeId']]
from gmusicapi import Mobileclient from grooveshark import Client import sys api = Mobileclient() api.login(sys.argv[1], sys.argv[2]) groove_client = Client() playlist = groove_client.playlist(sys.argv[3]) gp_songs=[] for song in playlist.songs: query = song.name + " " + song.artist.name gp_query_result = api.search_all_access( query, 1) try: track = gp_query_result['song_hits'][0]['track'] print( "Adding " + track['title'] + " by " + track['artist'] ) gp_songs.append(track['nid']) except IndexError as e: print("Coudn't find " + query); pass except: print(e) pass gp_playlist = api.create_playlist(playlist.name) api.add_songs_to_playlist(gp_playlist, gp_songs)
data = {'zipCode': zipcode, 'radius': radius_in_miles, 'page': 0, 'api_key': jambase_api_key} response = requests.get("http://api.jambase.com/events", params=data) data = response.json() for event in data['Events']: for artist in event['Artists']: artist_name = artist['Name'] print "\n", artist_name, "@", event['Venue']['Name'], # search Google All Access for the artist result = gapi.search_all_access(artist_name) # look at the first result for google_artist_data in result.get('artist_hits', []): google_artist = google_artist_data['artist'] google_artist_id = google_artist['artistId'] print "----> %s (%0.2f)" % (google_artist['name'], google_artist_data['score']) if google_artist_data['score'] > 200: # TODO: confirm high string similarity, as sometimes Google gives high scores to # strange matches. song_data = gapi.get_artist_info(google_artist_id, include_albums=False, max_top_tracks=5, max_rel_artist=0) for top_track in song_data.get('topTracks', []): #if top_track['genre'] in banned_genres: # continue print " + %s [%s]" % (top_track['title'], top_track.get('genre','N/A')) song_id = top_track.get('id') or top_track.get('nid')
class GMusic(object): def __init__(self): self.authenticated = False self.all_access = False self._device = None self._webclient = Webclient(debug_logging=False) self._mobileclient = Mobileclient(debug_logging=False) self._playlists = [] self._playlist_contents = [] self._all_songs = [] self._all_artists = {} self._all_albums = {} self._all_genres = {} self._stations = [] def _get_device_id(self): if self.authenticated: devices = self._webclient.get_registered_devices() for dev in devices: if dev['type'] == 'PHONE': self._device = dev['id'][2:] break def _set_all_access(self): settings = self._webclient._make_call(webclient.GetSettings, '') self.all_access = True if 'isSubscription' in settings['settings'] and settings['settings']['isSubscription'] == True else False def authenticate(self, email, password): try: mcauthenticated = self._mobileclient.login(email, password) except AlreadyLoggedIn: mcauthenticated = True try: wcauthenticated = self._webclient.login(email, password) except AlreadyLoggedIn: wcauthenticated = True self.authenticated = mcauthenticated and wcauthenticated self._get_device_id() self._set_all_access() return self.authenticated def get_all_songs(self, id=None): if len(self._all_songs) == 0: try: self._all_songs = self._mobileclient.get_all_songs() except NotLoggedIn: if self.authenticate(): self._all_songs = self._mobileclient.get_all_songs() else: return [] if id: return [x for x in self._all_songs if x['id'] == id][0] else: return self._all_songs def get_all_artists(self): if not self._all_artists: songs = self.get_all_songs() for song in songs: artist = song['artist'] thumb = None if artist not in self._all_artists: self._all_artists[artist] = [] track = {'title': song['title'], 'album': song['album'], 'artist': artist, 'durationMillis': song['durationMillis'], 'trackType': song['trackNumber'], 'id': song['id']} if 'albumArtRef' in song: track['albumArtRef'] = song['albumArtRef'] if 'artistArtRef' in song: thumb = song['artistArtRef'][0]['url'] if 'storeId' in song: track['storeId'] = song['storeId'] self._all_artists[artist].append({'track': track, 'thumb': thumb, 'id': song['id']}) return self._all_artists def get_all_albums(self): if not self._all_albums: songs = self.get_all_songs() for song in songs: album = song['album'] thumb = None if album not in self._all_albums: self._all_albums[album] = [] track = {'title': song['title'], 'album': album, 'artist': song['artist'], 'durationMillis': song['durationMillis'], 'trackType': song['trackNumber'], 'id': song['id']} if 'albumArtRef' in song: track['albumArtRef'] = song['albumArtRef'] thumb = song['albumArtRef'][0]['url'] if 'storeId' in song: track['storeId'] = song['storeId'] self._all_albums[album].append({'track': track, 'thumb': thumb, 'id': song['id']}) return self._all_albums def get_all_genres(self): if not self._all_genres: songs = self.get_all_songs() for song in songs: genre = song['genre'] if genre not in self._all_genres: self._all_genres[genre] = [] track = {'title': song['title'], 'album': song['album'], 'artist': song['artist'], 'durationMillis': song['durationMillis'], 'trackType': song['trackNumber'], 'id': song['id']} if 'albumArtRef' in song: track['albumArtRef'] = song['albumArtRef'] if 'storeId' in song: track['storeId'] = song['storeId'] self._all_genres[genre].append({'track': track, 'id': song['id']}) return self._all_genres def get_all_playlists(self): if len(self._playlists) == 0: try: self._playlists = self._mobileclient.get_all_playlists() except NotLoggedIn: if self.authenticate(): self._playlists = self._mobileclient.get_all_playlists() else: return [] return self._playlists def get_all_user_playlist_contents(self, id): tracks = [] if len(self._playlist_contents) == 0: try: self._playlist_contents = self._mobileclient.get_all_user_playlist_contents() except NotLoggedIn: if self.authenticate(): self._playlist_contents = self._mobileclient.get_all_user_playlist_contents() else: return [] for playlist in self._playlist_contents: if id == playlist['id']: tracks = playlist['tracks'] break return tracks def get_shared_playlist_contents(self, token): playlist = [] try: playlist = self._mobileclient.get_shared_playlist_contents(token) except NotLoggedIn: if self.authenticate(): playlist = self._mobileclient.get_shared_playlist_contents(token) else: return [] return playlist def get_all_stations(self): if len(self._stations) == 0: try: self._stations = self._mobileclient.get_all_stations() except NotLoggedIn: if self.authenticate(): self._stations = self._mobileclient.get_all_stations() else: return [] return self._stations def get_station_tracks(self, id, num_tracks=200): tracks = [] try: tracks = self._mobileclient.get_station_tracks(id, num_tracks) except NotLoggedIn: if self.authenticate(): tracks = self._mobileclient.get_station_tracks(id, num_tracks) else: return [] return tracks def get_genres(self): genres = [] try: genres = self._mobileclient.get_genres() except NotLoggedIn: if self.authenticate(): genres = self._mobileclient.get_genres() else: return [] return genres def create_station(self, name, id): station = None try: station = self._mobileclient.create_station(name=name, genre_id=id) except NotLoggedIn: if self.authenticate(): station = self._mobileclient.create_station(name=name, genre_id=id) else: return [] return station def search_all_access(self, query, max_results=50): results = None try: results = self._mobileclient.search_all_access(query, max_results) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.search_all_access(query, max_results) else: return [] return results def get_artist_info(self, id, include_albums=True, max_top_tracks=5, max_rel_artist=5): results = None try: results = self._mobileclient.get_artist_info(id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.get_artist_info(id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist) else: return [] return results def get_album_info(self, id, include_tracks=True): results = None try: results = self._mobileclient.get_album_info(id, include_tracks=include_tracks) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.get_album_info(id, include_tracks=include_tracks) else: return [] return results def get_stream_url(self, id): try: stream_url = self._mobileclient.get_stream_url(id, self._device) except NotLoggedIn: if self.authenticate(): stream_url = self._mobileclient.get_stream_url(id, self._device) else: return '' except CallFailure: raise CallFailure('Could not play song with id: ' + id, 'get_stream_url') return stream_url
if 'nid' not in t or t['nid'][0] != 'T': missing.append(info) def summary(track): print title(track) if 'nid' in track and track['nid'].startswith('T'): print 'https://play.google.com/music/m/%s' % track['nid'] def title(track): return "%35.33s / %.20s / \"%.20s\"" % (track['album'], track['artist'], track['title']) nomatch = '' toomany = [] found = [] for t in missing: res = (mc.search_all_access('"%s" "%s"' % (t['artist'], t['title'])))['song_hits'] if len(res) == 0: nomatch += title(t) + "\n" continue if t['nid'] in [x['track'].get('nid', None) for x in res]: found.append(title(t)) continue if len(res) < 10: summary(t) print '-' * 80 print "%d possible suggestions:" % len(res) for r in res: summary(r['track']) print "\n\n" continue toomany.append((title(t), len(res)))
import sys from gmusicapi import Mobileclient if __name__ == "__main__": if sys.argv[1] == "1": mc = Mobileclient() success = mc.login(sys.argv[3], sys.argv[4]) if success == True: sresults = mc.search_all_access(sys.argv[2], 1) #2 = query song = sresults['song_hits'][0] sid = song['track']['nid'] sname = song['track']['title'] sartist = song['track']['artist'] sartistid = song['track']['artistId'][0] salbum = song['track']['album'] salbumart = song['track']['albumArtRef'][0]['url'] aresults = mc.get_artist_info(sartistid, False, 1, 1) artistart = aresults['artistArtRef'] print(sid) print(sname) print(sartist) print(salbum) print(salbumart) print(artistart) if sys.argv[1] == "2": print("Spotify is not yet supported.")
class GoogleMusicClient(object): def __init__(self, username, password): self._username = username self._password = password self._apiClient = None def initConnection(self): self._apiClient = Mobileclient(debug_logging=False) if not self._apiClient.login(self._username, self._password): raise RuntimeError("Could not connect %s to Google Music." % \ self._username) def addPlaylist(self, playlist): plid = self._apiClient.create_playlist(playlist.name) tids = [] for track in playlist.tracks: aaid = self._getAllAccessTrackId(track) if aaid: try: tid = self._apiClient.add_aa_track(aaid) if tid: tids.append(tid) else: LOGGER.warning( "Could not add track %s to library.", str(track)) except: LOGGER.error("Could not add track %s to library.", str(track)) continue else: LOGGER.warning("Track %s not found.", str(track)) self._apiClient.add_songs_to_playlist(plid, tids) def addAlbum(self, album): aaid = self._getAllAccessAlbumId(album) if aaid: albumInfo = self._apiClient.get_album_info(aaid, include_tracks=True) for track in albumInfo["tracks"]: try: self._apiClient.add_aa_track(track[Track.GM_ID_KEY]) except: LOGGER.error("Could not add track %s to library.", str(track)) continue else: LOGGER.warning("Album %s not found.", str(album)) def _getAllAccessFirstResult(self, resource): queryStr = re.sub("[-:\(\)\",]","", "%s %s" % (resource.name, resource.artist)) queryStr = re.sub("\s+", "+", queryStr) searchResults = self._apiClient.search_all_access(queryStr) gmusicResources = searchResults.get(resource.GM_HITS_KEY) if gmusicResources: firstResult = gmusicResources[0][resource.GM_NAME] return firstResult[resource.GM_ID_KEY] else: return None def _getAllAccessTrackId(self, track): return self._getAllAccessFirstResult(track) def _getAllAccessAlbumId(self, album): return self._getAllAccessFirstResult(album)
from gmusicapi import Mobileclient from getpass import getpass import json # fill this in email = '' api = Mobileclient() logged_in = api.login(email, getpass()) if logged_in: for track in json.load(open('dump.json')): title = track['title'] artist = track['artist'] query = u"{title!s} - {artist!s}".format(**locals()) results = api.search_all_access(query)['song_hits'] if len(results) > 0: storeId = results[0]['track']['storeId'] api.add_aa_track(storeId) else: print u"Google Music lacks: {query!s}".format(**locals()) else: print "failed to log in"
def summary(track): print title(track) if 'nid' in track and track['nid'].startswith('T'): print 'https://play.google.com/music/m/%s' % track['nid'] def title(track): return "%35.33s / %.20s / \"%.20s\"" % (track['album'], track['artist'], track['title']) nomatch = '' toomany = [] found = [] for t in missing: res = (mc.search_all_access('"%s" "%s"' % (t['artist'], t['title'])))['song_hits'] if len(res) == 0: nomatch += title(t) + "\n" continue if t['nid'] in [x['track'].get('nid', None) for x in res]: found.append(title(t)) continue if len(res) < 10: summary(t) print '-' * 80 print "%d possible suggestions:" % len(res) for r in res: summary(r['track']) print "\n\n" continue toomany.append((title(t), len(res)))
class GoogleMusicApi: def __init__(self): self._api = Mobileclient() def login(self, username, password, device_id): try: return self._api.login(username, password, device_id) except AlreadyLoggedIn: Logger.debug('API: Already logged in') return True def relogin(self, username, password, device_id): try: return self._api.login(username, password, device_id) except AlreadyLoggedIn: self._api.logout() return self._api.login(username, password, device_id) def logout(self): return self._api.logout() def get_registered_mobile_devices(self): devices = self._api.get_registered_devices() mobile_devices = [] for device in devices: if device['type'] == "ANDROID": # TODO: Add iOS mobile_devices.append({ 'name': device['friendlyName'], 'id': device['id'][2:] }) return mobile_devices def get_stream_url(self, track_id, quality): return self._api.get_stream_url(song_id=track_id, quality=quality) def get_library(self): return self._api.get_all_songs() def get_album_info(self, album_id): return self._api.get_album_info(album_id) def search(self, query, max_results=25): # TODO: make number of results configurable / add to settings try: return self._api.search_all_access(query, max_results) # TODO: remove when gmusicapi 9.0.1 is stable except AttributeError: # develop version of gmusicapi is installed return self._api.search(query, max_results) def get_station_tracks(self, title, seed, num_tracks=25, recently_played_ids=None): # TODO: make number of results configurable / add to settings # TODO: check for existing stations, so we don't always create new ones (maybe not necessary: stations created with same seed have the same id seed_type = seed['type'] seed = seed['seed'] station_id = '' Logger.debug('Station: Creating station (Title: {}, Seed: {}, Type:{}'.format(title, seed, seed_type)) if seed_type == 'track': station_id = self.create_station(title, track_id=seed) elif seed_type == 'artist': station_id = self.create_station(title, artist_id=seed) elif seed_type == 'album': station_id = self.create_station(title, album_id=seed) elif seed_type == 'genre': station_id = self.create_station(title, genre_id=seed) elif seed_type == 'curated': Logger.debug("Station: CuratedStationId seed, don't know what to do :(") else: Logger.error("Station: Unknown seed, don't know what to do :(") if station_id: Logger.debug('Station: ID is ' + station_id) station_tracks = self._api.get_station_tracks(station_id, num_tracks, recently_played_ids) Logger.debug('Station: Station has {} tracks'.format(len(station_tracks))) return station_tracks else: Logger.warning("Station: Could not retrieve station ID") return [] def get_feeling_lucky_station_tracks(self, num_tracks=25, recently_played_ids=None): # TODO: make number of results configurable / add to settings return self._api.get_station_tracks('IFL', num_tracks, recently_played_ids) def create_station(self, name, track_id=None, artist_id=None, album_id=None, genre_id=None, playlist_token=None): return self._api.create_station(name, track_id=track_id, artist_id=artist_id, album_id=album_id, genre_id=genre_id, playlist_token=playlist_token) def increment_track_playcount(self, track_id): self._api.increment_song_playcount(track_id)
class GMusicClient(ContentConsumer): '''Element in charge of interfacing with GMusicApi Client''' def __init__(self, data_cache): self.client = Mobileclient() self.data_cache = data_cache def login(self): '''Use data/unlocked/credentials.json to log in''' mac = Mobileclient.FROM_MAC_ADDRESS credentials = json.load(open('data/unlocked/credentials.json', 'r')) self.client.login(credentials['username'], credentials['password'], mac) def load_my_library(self): '''Load user's songs, playlists, and stations''' track_load = threading.Thread(target=self.load_tracks) radio_load = threading.Thread(target=self.load_radios) playlist_load = threading.Thread(target=self.load_playlists) track_load.start() radio_load.start() playlist_load.start() track_load.join() radio_load.join() playlist_load.join() def load_playlists(self): playlists = self.client.get_all_user_playlist_contents() playlists.reverse() self.data_cache.playlists = playlists def load_tracks(self): self.data_cache.tracks = [t for t in self.client.get_all_songs() if 'nid' in t] def scrape_song(self, track): return track def load_radios(self): radios = self.client.get_all_stations() radios.reverse() self.data_cache.radios = radios def get_radio_contents(self, radio_id): tracks = self.client.get_station_tracks(radio_id) return tracks def get_radio_list(self, name): return [r for r in self.data_cache.radios if name in r['name']] def filter(self, element, field, filter_by): return [e for e in element if filter_by in e[field]] def get_playlist_list(self, name): return self.filter(self.data_cache.playlists, 'name', name) def search_all_access(self, query): return self.client.search_all_access(query) def create_radio(self, seed_type, id, name): '''Create a radio''' # This is a weird way to do this, but there's no other choice ids = {"track": None, "album": None, "artist": None} seed_id_name = self.get_type_name(seed_type) ids[seed_id_name] = id return self.client.create_station(name=name, track_id=ids['track'], album_id=ids['album'], artist_id=ids['artist']) def search_items_all_access(self, type, query): '''Searches Albums, Artists, and Songs; uses metaprogramming''' index_arguments = self.get_index_arguments(type) items = self.search_all_access(query)['{0}_hits'.format(type[:-1])] return [self.format_item(item, type, index_arguments) for item in items] def get_sub_items(self, type_from, search_type, from_id): '''Here type_from refers to artist or album we're indexing against''' args = self.get_index_arguments(search_type) if type_from == 'playlist': return self.get_playlist_contents(from_id) else: # Get the appropriate search method and execute it search_method_name = 'get_{0}_info'.format(type_from) search_method = getattr(self.client, search_method_name) try: items = search_method(from_id, True)[args['type']+'s'] # True here includes subelements except: # User passed in a bad id or something return # Now return appropriately return [self.format_subitems(t, args) for t in items if args['id'] in t] def get_playlist_contents(self, from_id): '''Playlist exclusive stuff''' shareToken = [t for t in self.data_cache.playlists \ if t['id'] == from_id][0]['shareToken'] contents = self.client.get_shared_playlist_contents(shareToken) return [self.format_playlist_contents(t) for t in contents if 'track' in t] def get_suggested(self): '''Returns a list of tracks that the user might be interested in''' items = sorted(self.client.get_promoted_songs(), key=lambda y: y['title']) return [self.format_suggested(t) for t in items if 'storeId' in t] def get_information_about(self, from_type, from_id): '''Gets specific information about an id (depending on the type)''' if 'artist' in from_type.lower(): return self.client.get_artist_info(from_id, include_albums=False) if 'album' in from_type.lower(): return self.client.get_album_info(from_id, include_tracks=False) return self.client.get_track_info(from_id) def get_stream_url(self, nid): return self.client.get_stream_url(nid) def lookup(self, nid): return self.client.get_track_info(nid) def add_track_to_library(self, nid): self.client.add_aa_track(nid) def add_to_playlist(self, playlist_id, nid): self.client.add_songs_to_playlist(playlist_id, nid) playlist_load = threading.Thread(target=self.load_playlists) playlist_load.daemon = True playlist_load.start() def format_suggested(self, t): return (t['title'], t['storeId'], 'Play', t['album']) def format_playlist_contents(self, t): return (t['track']['title'], t['trackId'], 'Play', t['track']['album']) def format_subitems(self, t, args): return (t[args['name']], t[args['id']], args['command'], t[args['alt']])
class MusicManager(object): def __init__(self): self.api = Mobileclient(validate=False, debug_logging=False) if config.GOOGLE_STREAMKEY is not None: self.api.login(config.GOOGLE_USERNAME, config.GOOGLE_PASSWORD, config.GOOGLE_STREAMKEY) else: self.api.login(config.GOOGLE_USERNAME, config.GOOGLE_PASSWORD, Mobileclient.FROM_MAC_ADDRESS) self.queue = [] self.current_index = len(self.queue) - 1 self.vlc = VlcManager() self.state_thread = Thread(target=self.check_state) self.state_thread.daemon = True self.state_thread.start() def play_song(self, id): song = self.queue_song(id) self.current_index = len(self.queue) - 1 self.load_song() return song def queue_song(self, id): self.queue.append(self.getSongInfo(id)) def play_radio_station(self, id): results = self.api.get_station_tracks(id, num_tracks=40) for song in results: song['albumArtRef'] = song['albumArtRef'][0]['url'] if 'artistId' in song: song['artistId'] = song['artistId'][0] self.current_index = len(self.queue) - 1 self.queue.append(results) self.load_song() def play_album(self, args): album = self.get_album_details(args) songs = [] for index in range(len(album['tracks'])): song = album['tracks'][index] if index == 0: songs.append(self.play_song(song['nid'])) else: songs.append(self.queue_song(song['nid'])) return songs def queue_album(self, args): album = self.get_album_details(args) songs = [] for song in album['tracks']: songs.append(self.queue_song(song['nid'])) return songs def next(self): self.current_index += 1 self.load_song() def prev(self): self.current_index -= 1 self.load_song() def pause(self): self.vlc.vlc_pause() def resume(self): self.vlc.vlc_resume() def volume(self, val): self.vlc.vlc_volume(val) def delete(self, id): if id > self.current_index: del self.queue[id] elif id < self.current_index: del self.queue[id] self.current_index -= 1 else: del self.queue[id] self.load_song() def go_to(self, id): self.current_index = id self.load_song() def load_song(self): if self.current_index < len(self.queue): song = self.queue[self.current_index] url = self.api.get_stream_url(song['nid'], config.GOOGLE_STREAMKEY) self.vlc.vlc_play(url) def check_state(self): while True: status = self.vlc.player.get_state() if status == vlc.State.Ended: if self.current_index != len(self.queue) - 1: self.next() time.sleep(1) def get_status(self): status = self.vlc.vlc_status() # status['queue'] = self.queue[:] # for i in range(len(status['queue'])): # status['queue'][i]['vlcid'] = i # if i == self.current_index: # status['queue'][i]['current'] = True # status['current'] = status['queue'][i] if len(self.queue) > 0: status['current'] = self.queue[self.current_index] return status def get_queue(self): queue = self.queue[:] for i in range(len(queue)): queue[i]['vlcid'] = i return queue def search(self, query): results = self.api.search_all_access(query, max_results=50) results['artist_hits'] = [artist['artist'] for artist in results['artist_hits']] results['album_hits'] = [album['album'] for album in results['album_hits']] for album in results['album_hits']: album['artistId'] = album['artistId'][0] results['song_hits'] = [song['track'] for song in results['song_hits']] for song in results['song_hits']: song['albumArtRef'] = song['albumArtRef'][0]['url'] if 'artistId' in song: song['artistId'] = song['artistId'][0] return results def get_album_details(self, id): results = self.api.get_album_info(album_id=id, include_tracks=True) results['artistId'] = results['artistId'][0] for song in results['tracks']: song['albumArtRef'] = song['albumArtRef'][0]['url'] if 'artistId' in song: song['artistId'] = song['artistId'][0] return results def get_artist_details(self, id): results = self.api.get_artist_info(artist_id=id) for album in results['albums']: album['artistId'] = album['artistId'][0] for song in results['topTracks']: song['albumArtRef'] = song['albumArtRef'][0]['url'] if 'artistId' in song: song['artistId'] = song['artistId'][0] return results def create_radio_station(self, name, id): if id[0] == 'A': station_id = self.api.create_station(name, artist_id=id) elif id[0] == 'B': station_id = self.api.create_station(name, album_id=id) else: station_id = self.api.create_station(name, track_id=id) return station_id def get_radio_stations(self): return self.api.get_all_stations() def flush(self): self.vlc.vlc_stop() self.queue = [] def getSongInfo(self, id): song = self.api.get_track_info(id) song['albumArtRef'] = song['albumArtRef'][0]['url'] if 'artistId' in song: song['artistId'] = song['artistId'][0] return song
class GoogleMusic(object): def __init__(self): self.webclient = Webclient() self.mobileclient = Mobileclient() def is_authenticated(self): if self.webclient.is_authenticated(): if self.mobileclient.is_authenticated(): return True return False def login(self, username, password): if not self.is_authenticated(): try: self.mobileclient.login(username, password) self.webclient.login(username, password) except: raise Exception('Couldn\'t log into Google Music') def search(self, query, kind): if self.is_authenticated(): results = self.mobileclient.search_all_access(query)[kind + '_hits'] return results def get_track(self, store_id): return self.mobileclient.get_track_info(store_id) def save_stream(self, track, destination): if self.is_authenticated(): with open(destination, 'w+b') as stream_file: urls = self.webclient.get_stream_urls(track.get('storeId')) if len(urls) == 1: stream_file.write(self.webclient.session._rsession.get(urls[0]).content) 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'] for url, (start, end) in zip(urls, range_pairs): stream_file.truncate(start) stream_file.seek(0, 2) audio = self.webclient.session._rsession.get(url).content stream_file.write(audio) tag = easyid3.EasyID3() tag['title'] = track.get('title').__str__() tag['artist'] = track.get('artist').__str__() tag['album'] = track.get('album').__str__() tag['date'] = track.get('year').__str__() tag['discnumber'] = track.get('discNumber').__str__() tag['tracknumber'] = track.get('trackNumber').__str__() tag['performer'] = track.get('albumArtist').__str__() tag.save(destination) tag = mp3.MP3(destination) tag.tags.add( id3.APIC(3, 'image/jpeg', 3, 'Front cover', urllib.urlopen(track.get('albumArtRef')[0].get('url')).read()) ) tag.save()
class GMusicClient(ContentConsumer): """Element in charge of interfacing with GMusicApi Client""" def __init__(self, data_cache): self.client = Mobileclient() self.data_cache = data_cache def login(self): """Use data/unlocked/credentials.json to log in""" mac = Mobileclient.FROM_MAC_ADDRESS try: credentials = json.load(open("data/unlocked/credentials.json", "r")) result = self.client.login(credentials["username"], credentials["password"], mac) if result == False: print("\n\033[93mLogin failed.") print("Please double check that data/unlocked/credentials.json has correct information.\033[m\n") os._exit(1) except: print("\n\033[93mdata/unlocked/credentials.json is not valid.") print("You may need to delete and regnerate the credentials file.\033[m\n") exit(1) def load_my_library(self): """Load user's songs, playlists, and stations""" track_load = threading.Thread(target=self.load_tracks) radio_load = threading.Thread(target=self.load_radios) playlist_load = threading.Thread(target=self.load_playlists) track_load.start() radio_load.start() playlist_load.start() track_load.join() radio_load.join() playlist_load.join() def load_playlists(self): playlists = self.client.get_all_user_playlist_contents() playlists.reverse() self.data_cache.playlists = playlists def load_tracks(self): self.data_cache.tracks = [t for t in self.client.get_all_songs() if "nid" in t] def scrape_song(self, track): return track def load_radios(self): radios = self.client.get_all_stations() radios.reverse() self.data_cache.radios = radios def get_radio_contents(self, radio_id): tracks = self.client.get_station_tracks(radio_id) return tracks def get_radio_list(self, name): return [r for r in self.data_cache.radios if name in r["name"]] def filter(self, element, field, filter_by): return [e for e in element if filter_by in e[field]] def get_playlist_list(self, name): return self.filter(self.data_cache.playlists, "name", name) def search_all_access(self, query): return self.client.search_all_access(query) def create_radio(self, seed_type, id, name): """Create a radio""" # This is a weird way to do this, but there's no other choice ids = {"track": None, "album": None, "artist": None} seed_id_name = self.get_type_name(seed_type) ids[seed_id_name] = id return self.client.create_station( name=name, track_id=ids["track"], album_id=ids["album"], artist_id=ids["artist"] ) def search_items_all_access(self, type, query): """Searches Albums, Artists, and Songs; uses metaprogramming""" index_arguments = self.get_index_arguments(type) items = self.search_all_access(query)["{0}_hits".format(type[:-1])] return [self.format_item(item, type, index_arguments) for item in items] def get_sub_items(self, type_from, search_type, from_id): """Here type_from refers to artist or album we're indexing against""" args = self.get_index_arguments(search_type) if type_from == "playlist": return self.get_playlist_contents(from_id) else: # Get the appropriate search method and execute it search_method_name = "get_{0}_info".format(type_from) search_method = getattr(self.client, search_method_name) try: items = search_method(from_id, True)[args["type"] + "s"] # True here includes subelements except: # User passed in a bad id or something return # Now return appropriately return [self.format_subitems(t, args) for t in items if args["id"] in t] def get_playlist_contents(self, from_id): """Playlist exclusive stuff""" shareToken = [t for t in self.data_cache.playlists if t["id"] == from_id][0]["shareToken"] contents = self.client.get_shared_playlist_contents(shareToken) return [self.format_playlist_contents(t) for t in contents if "track" in t] def get_suggested(self): """Returns a list of tracks that the user might be interested in""" items = sorted(self.client.get_promoted_songs(), key=lambda y: y["title"]) return [self.format_suggested(t) for t in items if "storeId" in t] def get_information_about(self, from_type, from_id): """Gets specific information about an id (depending on the type)""" if "artist" in from_type.lower(): return self.client.get_artist_info(from_id, include_albums=False) if "album" in from_type.lower(): return self.client.get_album_info(from_id, include_tracks=False) return self.client.get_track_info(from_id) def get_stream_url(self, nid): return self.client.get_stream_url(nid) def lookup(self, nid): return self.client.get_track_info(nid) def add_track_to_library(self, nid): self.client.add_aa_track(nid) def add_to_playlist(self, playlist_id, nid): self.client.add_songs_to_playlist(playlist_id, nid) playlist_load = threading.Thread(target=self.load_playlists) playlist_load.daemon = True playlist_load.start() def format_suggested(self, t): return (t["title"], t["storeId"], "Play", t["album"]) def format_playlist_contents(self, t): return (t["track"]["title"], t["trackId"], "Play", t["track"]["album"]) def format_subitems(self, t, args): return (t[args["name"]], t[args["id"]], args["command"], t[args["alt"]])
from gmusicapi import Mobileclient from grooveshark import Client import sys api = Mobileclient() api.login(sys.argv[1], sys.argv[2]) groove_client = Client() playlist = groove_client.playlist(sys.argv[3]) gp_songs = [] for song in playlist.songs: query = song.name + " " + song.artist.name gp_query_result = api.search_all_access(query, 1) try: track = gp_query_result['song_hits'][0]['track'] print("Adding " + track['title'] + " by " + track['artist']) gp_songs.append(track['nid']) except IndexError as e: print("Coudn't find " + query) pass except: print(e) pass gp_playlist = api.create_playlist(playlist.name) api.add_songs_to_playlist(gp_playlist, gp_songs)
class Playlist(): api = None dryrun = False def __init__(self, credentials_path = DEFAULT_CREDS, user = None, dryrun = False, **kwargs): self.dryrun = dryrun self.api = Mobileclient() password = None if not user: user, password = self._loadCredentials(credentials_path) else: password = self._queryPass(user) logged_in = self.api.login(user, password) if not logged_in: print("Login failed, exiting") sys.exit(1) def _queryPass(self, user): return getpass("Provide password for user %s: " % user) def _loadCredentials(self, path = DEFAULT_CREDS): credentials = anymarkup.parse_file(path) return credentials["credentials"]["user"], credentials["credentials"]["password"] def _searchForSong(self, song_item): query = "%(artist)s %(title)s" % song_item result = self.api.search_all_access(query, 10) print("Query: %s, found %d hits" % (query, len(result["song_hits"]))) first = None for item in result["song_hits"]: track = item["track"] if not first: first = track if (song_item["title"] in track["title"] or track["title"] in song_item["title"]) and (song_item["artist"] in track["albumArtist"] or\ song_item["artist"] in track["artist"] or\ track["artist"] in song_item["artist"] or\ track["albumArtist"] in song_item["artist"]\ ): return track if first: print("=> Given title and artist don't match, using first result: %s - %s" % (first["title"], first["artist"])) return first return None def _createPlaylist(self): name = "BBC Radio 1 Chart %s" % time.strftime("%Y-%m-%d") return self.api.create_playlist(name, public=True) def _addToPlaylist(self, playlist_id, song_item): self.api.add_songs_to_playlist(playlist_id, song_item["nid"]) def create(self, songs_list): if not self.dryrun: playlist_id = self._createPlaylist() for song_item in songs_list: song = self._searchForSong(song_item) if not song: print("=> Couldn't find %s" % song_item) continue if not self.dryrun: self._addToPlaylist(playlist_id, song)
pw = getpass.getpass() if not client.login(args.username, pw, Mobileclient.FROM_MAC_ADDRESS): print("Authentication failed. Please check the provided credentials.") with open(args.source) as f: data = json.load(f) if args.dryrun: print("[/!\] We're currently running in dry-run mode") for playlist in data["playlists"]: if args.dryrun: print("Checking importability of %s" % playlist["title"]) else: print("Importing %s" % playlist["title"]) toimport = [] for track in playlist["tracks"]: query = "%s %s" % (track["title"], track["artist"]) results = client.search_all_access(query) match = None if args.verbose: print("Fetching matches for %s" % query) for hit_i, hit in enumerate(results["song_hits"]): trackDeets = hit["track"]["title"] match = hit["track"]["storeId"] print("Found match:\n%s" % trackDeets) break if match is not None: toimport.append(match) else: print("[!!!] No good match for %s" % query) if not args.dryrun and toimport: playlist_id = client.create_playlist(playlist["title"]) client.add_songs_to_playlist(playlist_id, toimport)
class MusicSync(object): def __init__(self, email=None, password=None): self.mm = Musicmanager() self.wc = Webclient() self.mc = Mobileclient() if not email: email = raw_input("Email: ") if not password: password = getpass() self.email = email self.password = password self.logged_in = self.auth() print "Fetching playlists from Google..." self.playlists = self.wc.get_all_playlist_ids(auto=False) print "Got %d playlists." % len(self.playlists["user"]) print "" def auth(self): self.logged_in = self.wc.login(self.email, self.password) self.logged_in = self.mc.login(self.email, self.password) if not self.logged_in: print "Login failed..." exit() print "" print "Logged in as %s" % self.email print "" if not os.path.isfile(OAUTH_FILEPATH): print "First time login. Please follow the instructions below:" self.mm.perform_oauth() self.logged_in = self.mm.login() if not self.logged_in: print "OAuth failed... try deleting your %s file and trying again." % OAUTH_FILEPATH exit() print "Authenticated" print "" def add_rhapsody_playlist(self, filename, remove_missing=False): filename = self.get_platform_path(filename) os.chdir(os.path.dirname(filename)) # playlist_title = os.path.splitext(os.path.basename(filename))[0] print "Synching File: %s" % filename print "Parsing Songs from %s" % filename pc_songs = self.get_songs_from_file(filename) # print (pc_songs) print "%d songs in local file: %s" % (len(pc_songs), filename) # Sanity check max 1000 songs per playlist if len(pc_songs) > MAX_SONGS_IN_PLAYLIST: print " Google music doesn't allow more than %d songs in a playlist..." % MAX_SONGS_IN_PLAYLIST print " Will only attempt to sync the first %d songs." % MAX_SONGS_IN_PLAYLIST del pc_songs[MAX_SONGS_IN_PLAYLIST:] existing_files = 0 added_files = 0 failed_files = 0 removed_files = 0 fatal_count = 0 for song in pc_songs: playlist_title = song["playlist"] if playlist_title not in self.playlists["user"]: self.playlists["user"][playlist_title] = [self.mc.create_playlist(playlist_title)] time.sleep(0.7) print "Starting Playlist Sync with Google music..." for song in pc_songs: # print song plid = "" print "--------------------------------" print "" print "Playlist: %s" % song["playlist"] print "Artist: %s" % song["artist"] print "Song: %s" % song["title"] print "Album: %s" % song["album"] playlist_title = song["playlist"] plid = self.playlists["user"][playlist_title][0] goog_songs = self.wc.get_playlist_songs(plid) if self.song_already_in_list(song, goog_songs): existing_files += 1 print "Result: Song Already Added" continue print "Total %d songs in Google playlist: %s" % (len(goog_songs), playlist_title) print "%s - %s, didn't exist...Will try to add..." % (song["artist"], song["title"]) print "" print "--------------------------------" results = self.mc.search_all_access(song["title"], max_results=50) nid = self.filter_search_results(results, song) print "AA nId: %s " % nid if nid: song_id = self.mc.add_aa_track(nid) added = self.wc.add_songs_to_playlist(plid, song_id) print "Playlist UUid: %s" % plid print "Song ID: %s" % song_id time.sleep(0.3) # Don't spam the server too fast... print "Result: done adding to playlist" added_files += 1 continue else: query = "%s %s" % (song["artist"], song["title"].split(" ")[0]) print "Query %s" % query results = self.mc.search_all_access(query, max_results=50) nid = self.filter_search_results(results, song) if nid: song_id = self.mc.add_aa_track(nid) added = self.wc.add_songs_to_playlist(plid, song_id) print "Playlist UUid: %s" % plid print "Song ID: %s" % song_id time.sleep(0.3) # Don't spam the server too fast... print " -- done adding to playlist" added_files += 1 continue print "Result: NID Blank, Song not Found in All Access" print "" print "---" print "%d songs unmodified" % existing_files print "%d songs added" % added_files print "%d songs failed" % failed_files print "%d songs removed" % removed_files def get_songs_from_file(self, filename): songs = [] f = codecs.open(filename, encoding="utf-8") for line in f: line = line.rstrip().replace(u"\ufeff", u"") if line == "" or line[0] == "#": continue la = line.split("\\") regex_filter = "[^A-Za-z0-9\,\-\.\ \(\)'\!\?\$\/ \& \:]" artist = re.sub(regex_filter, "", la[1]) playlist = re.sub(regex_filter, "", la[0]) album = re.sub(regex_filter, "", la[2]) title = re.sub(regex_filter, "", la[3]) # print "Filtered Strings:" # print "Artist: %s" % artist # print "Playlist: %s" % playlist # print "Song: %s" % title # print "Album: %s" % album dt = {"playlist": playlist, "artist": artist, "album": album, "title": title} # print (dt) songs.append(dt) f.close() return songs def get_songs_from_playlist(self, filename): songs = [] f = codecs.open(filename, encoding="utf-8") for line in f: line = line.rstrip().replace(u"\ufeff", u"") if line == "" or line[0] == "#": continue path = os.path.abspath(self.get_platform_path(line)) # if not os.path.exists(path): # print "File not found: %s" % line # continue songs.append(path) f.close() return songs def get_files_from_playlist(self, filename): files = [] f = codecs.open(filename, encoding="utf-8") for line in f: line = line.rstrip().replace(u"\ufeff", u"") if line == "" or line[0] == "#": continue path = os.path.abspath(self.get_platform_path(line)) if not os.path.exists(path): print "File not found: %s" % line continue files.append(path) f.close() return files def song_already_in_list(self, song, goog_songs): # tag = self.get_id3_tag(filename) i = 0 while i < len(goog_songs): # print goog_songs if self.tag_compare(goog_songs[i], song): goog_songs.pop(i) return True i += 1 return False def file_already_in_list(self, filename, goog_songs): tag = self.get_id3_tag(filename) i = 0 while i < len(goog_songs): if self.tag_compare(goog_songs[i], tag): goog_songs.pop(i) return True i += 1 return False def get_id3_tag(self, filename): data = mutagen.File(filename, easy=True) r = {} if "title" not in data: title = os.path.splitext(os.path.basename(filename))[0] print "Found song with no ID3 title, setting using filename:" print " %s" % title print " (please note - the id3 format used (v2.4) is invisible to windows)" data["title"] = [title] data.save() r["title"] = data["title"][0] r["track"] = int(data["tracknumber"][0].split("/")[0]) if "tracknumber" in data else 0 # If there is no track, try and get a track number off the front of the file... since thats # what google seems to do... # Not sure how google expects it to be formatted, for now this is a best guess if r["track"] == 0: m = re.match("(\d+) ", os.path.basename(filename)) if m: r["track"] = int(m.group(0)) r["artist"] = data["artist"][0] if "artist" in data else "" r["album"] = data["album"][0] if "album" in data else "" return r def find_song(self, filename, plid): tag = self.get_id3_tag(filename) print "Song Tag: %s " % tag print "Filename: %s" % filename ws_plids = [] ws_plids.append(plid) print (ws_plids) playlists = self.wc.get_all_playlist_ids() print (playlists) results = self.wc.get_playlist_songs(ws_plids) # NOTE - dianostic print here to check results if you're creating duplicates print results print "%s ][ %s ][ %s ][ %s" % (tag["title"], tag["artist"], tag["album"], tag["track"]) for r in results: if self.tag_compare(r, tag): # TODO: add rough time check to make sure its "close" return r return None def filter_search_results(self, results, song): # Try Exact Matching for g_song in results["song_hits"]: if self.tag_compare(g_song["track"], song): return g_song["track"]["nid"] elif self.song_compare(g_song["track"], song, "artist"): # try just the artist return g_song["track"]["nid"] elif self.song_compare(g_song["track"], song, "part-song"): # try part of song and artist return g_song["track"]["nid"] return None def song_compare(self, g_song, tag, type): if "track" not in g_song: g_song["track"] = 0 title_parts = tag["title"].split("(") # removing shit like (featuring wiz) tp = title_parts[0].split(" ") # First word maybe if "artist" in type: return g_song["artist"].lower() == tag["artist"].lower() if "part-song" in type: return g_song["title"].find(tp[0]) and g_song["artist"].lower() == tag["artist"].lower() return None def tag_compare(self, g_song, tag): if "track" not in g_song: g_song["track"] = 0 return ( g_song["title"].split(" ")[0].lower() == tag["title"].split(" ")[0].lower() and g_song["artist"].lower() == tag["artist"].lower() ) def delete_song(self, sid): self.wc.delete_songs(sid) print "Deleted song by id [%s]" % sid def get_platform_path(self, full_path): # Try to avoid messing with the path if possible if os.sep == "/" and "\\" not in full_path: return full_path if os.sep == "\\" and "\\" in full_path: return full_path if "\\" not in full_path: return full_path return os.path.normpath(full_path.replace("\\", "/"))
class GMusic(object): def __init__(self): self.authenticated = False self.all_access = False self.library_loaded = False self.all_songs = [] self.letters = {} self.artists = {} self.albums = {} self.genres = {} self.tracks_by_letter = {} self.tracks_by_artist = {} self.tracks_by_album = {} self.tracks_by_genre = {} self._device = None self._webclient = Webclient(debug_logging=False) self._mobileclient = Mobileclient(debug_logging=False) self._playlists = [] self._playlist_contents = [] self._stations = [] def _get_device_id(self): if self.authenticated: devices = self._webclient.get_registered_devices() for dev in devices: if dev['type'] == 'PHONE': self._device = dev['id'][2:] break elif dev['type'] == 'IOS': self._device = dev['id'] break def _set_all_access(self): settings = self._webclient._make_call(webclient.GetSettings, '') self.all_access = True if 'isSubscription' in settings['settings'] and settings['settings']['isSubscription'] == True else False def _set_all_songs(self): if len(self.all_songs) == 0: try: self.all_songs = self._mobileclient.get_all_songs() except NotLoggedIn: if self.authenticate(): self.all_songs = self._mobileclient.get_all_songs() else: return [] else: return self.all_songs def authenticate(self, email, password): try: mcauthenticated = self._mobileclient.login(email, password) except AlreadyLoggedIn: mcauthenticated = True try: wcauthenticated = self._webclient.login(email, password) except AlreadyLoggedIn: wcauthenticated = True self.authenticated = mcauthenticated and wcauthenticated self._set_all_access() self._get_device_id() return self.authenticated def load_data(self): self._set_all_songs() for song in self.all_songs: thumb = None letter = song['title'][0] artist = song['artist'] album = song['album'] genre = song['genre'] if 'genre' in song else '(None)' if letter not in self.tracks_by_letter: self.tracks_by_letter[letter] = [] self.letters[letter] = None if artist not in self.tracks_by_artist: self.tracks_by_artist[artist] = [] self.artists[artist] = None if album not in self.tracks_by_album: self.tracks_by_album[album] = [] self.albums[album] = None if genre not in self.tracks_by_genre: self.tracks_by_genre[genre] = [] self.genres[genre] = None track = {'artist': artist, 'album': album} if 'title' in song: track['title'] = song['title'] if 'album' in song: track['album'] = song['album'] if 'artist' in song: track['artist'] = song['artist'] if 'durationMillis' in song: track['durationMillis'] = song['durationMillis'] if 'id' in song: track['id'] = song['id'] if 'trackNumber' in song: track['trackType'] = song['trackNumber'] if 'storeId' in song: track['storeId'] = song['storeId'] if 'albumArtRef' in song: track['albumArtRef'] = song['albumArtRef'] thumb = song['albumArtRef'][0]['url'] self.letters[letter] = thumb self.artists[artist] = thumb self.albums[album] = thumb self.genres[genre] = thumb self.tracks_by_letter[letter].append({'track': track, 'thumb': thumb, 'id': song['id']}) self.tracks_by_artist[artist].append({'track': track, 'thumb': thumb, 'id': song['id']}) self.tracks_by_album[album].append({'track': track, 'thumb': thumb, 'id': song['id']}) self.tracks_by_genre[genre].append({'track': track, 'thumb': thumb, 'id': song['id']}) self.library_loaded = True def get_tracks_for_type(self, type, name): type = type.lower() if type == 'artists': return self.tracks_by_artist[name] elif type == 'albums': return self.tracks_by_album[name] elif type == 'genres': return self.tracks_by_genre[name] elif type == 'songs by letter': return self.tracks_by_letter[name] else: return {} def get_song(self, id): return [x for x in self.all_songs if x['id'] == id][0] def get_all_playlists(self): if len(self._playlists) == 0: try: self._playlists = self._mobileclient.get_all_playlists() except NotLoggedIn: if self.authenticate(): self._playlists = self._mobileclient.get_all_playlists() else: return [] return self._playlists def get_all_user_playlist_contents(self, id): tracks = [] if len(self._playlist_contents) == 0: try: self._playlist_contents = self._mobileclient.get_all_user_playlist_contents() except NotLoggedIn: if self.authenticate(): self._playlist_contents = self._mobileclient.get_all_user_playlist_contents() else: return [] for playlist in self._playlist_contents: if id == playlist['id']: tracks = playlist['tracks'] break return tracks def get_shared_playlist_contents(self, token): playlist = [] try: playlist = self._mobileclient.get_shared_playlist_contents(token) except NotLoggedIn: if self.authenticate(): playlist = self._mobileclient.get_shared_playlist_contents(token) else: return [] return playlist def get_all_stations(self): if len(self._stations) == 0: try: self._stations = self._mobileclient.get_all_stations() except NotLoggedIn: if self.authenticate(): self._stations = self._mobileclient.get_all_stations() else: return [] return self._stations def get_station_tracks(self, id, num_tracks=200): tracks = [] try: tracks = self._mobileclient.get_station_tracks(id, num_tracks) except NotLoggedIn: if self.authenticate(): tracks = self._mobileclient.get_station_tracks(id, num_tracks) else: return [] return tracks def get_genres(self): genres = [] try: genres = self._mobileclient.get_genres() except NotLoggedIn: if self.authenticate(): genres = self._mobileclient.get_genres() else: return [] return genres def create_station(self, name, id): station = None try: station = self._mobileclient.create_station(name=name, genre_id=id) except NotLoggedIn: if self.authenticate(): station = self._mobileclient.create_station(name=name, genre_id=id) else: return [] return station def search_all_access(self, query, max_results=50): results = None try: results = self._mobileclient.search_all_access(query, max_results) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.search_all_access(query, max_results) else: return [] return results def get_artist_info(self, id, include_albums=True, max_top_tracks=5, max_rel_artist=5): results = None try: results = self._mobileclient.get_artist_info(id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.get_artist_info(id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist) else: return [] return results def get_album_info(self, id, include_tracks=True): results = None try: results = self._mobileclient.get_album_info(id, include_tracks=include_tracks) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.get_album_info(id, include_tracks=include_tracks) else: return [] return results def add_aa_track(self, id): track = None try: track = self._mobileclient.add_aa_track(id) except NotLoggedIn: if self.authenticate(): track = self._mobileclient.add_aa_track(id) else: return None return track def add_songs_to_playlist(self, playlist_id, song_ids): tracks = None try: tracks = self._mobileclient.add_songs_to_playlist(playlist_id, song_ids) except NotLoggedIn: if self.authenticate(): tracks = self._mobileclient.add_songs_to_playlist(playlist_id, song_ids) else: return None return tracks def get_stream_url(self, id): try: stream_url = self._mobileclient.get_stream_url(id, self._device) except NotLoggedIn: if self.authenticate(): stream_url = self._mobileclient.get_stream_url(id, self._device) else: return '' except CallFailure: raise CallFailure('Could not play song with id: ' + id, 'get_stream_url') return stream_url def reset_library(self): self.library_loaded = False self.all_songs[:] = [] self._playlists[:] = [] self._playlist_contents[:] = [] self._stations[:] = [] self.letters.clear() self.artists.clear() self.albums.clear() self.genres.clear() self.tracks_by_letter.clear() self.tracks_by_artist.clear() self.tracks_by_album.clear() self.tracks_by_genre.clear()
class tizgmusicproxy(object): """A class for logging into a Google Play Music account and retrieving song URLs. """ all_songs_album_title = "All Songs" thumbs_up_playlist_name = "Thumbs Up" def __init__(self, email, password, device_id): self.__gmusic = Mobileclient() self.__email = email self.__device_id = device_id self.logged_in = False self.queue = list() self.queue_index = -1 self.play_queue_order = list() self.play_modes = TizEnumeration(["NORMAL", "SHUFFLE"]) self.current_play_mode = self.play_modes.NORMAL self.now_playing_song = None userdir = os.path.expanduser('~') tizconfig = os.path.join(userdir, ".config/tizonia/." + email + ".auth_token") auth_token = "" if os.path.isfile(tizconfig): with open(tizconfig, "r") as f: auth_token = pickle.load(f) if auth_token: # 'Keep track of the auth token' workaround. See: # https://github.com/diraimondo/gmusicproxy/issues/34#issuecomment-147359198 print_msg("[Google Play Music] [Authenticating] : " \ "'with cached auth token'") self.__gmusic.android_id = device_id self.__gmusic.session._authtoken = auth_token self.__gmusic.session.is_authenticated = True try: self.__gmusic.get_registered_devices() except CallFailure: # The token has expired. Reset the client object print_wrn("[Google Play Music] [Authenticating] : " \ "'auth token expired'") self.__gmusic = Mobileclient() auth_token = "" if not auth_token: attempts = 0 print_nfo("[Google Play Music] [Authenticating] : " \ "'with user credentials'") while not self.logged_in and attempts < 3: self.logged_in = self.__gmusic.login(email, password, device_id) attempts += 1 with open(tizconfig, "a+") as f: f.truncate() pickle.dump(self.__gmusic.session._authtoken, f) self.library = CaseInsensitiveDict() self.song_map = CaseInsensitiveDict() self.playlists = CaseInsensitiveDict() self.stations = CaseInsensitiveDict() def logout(self): """ Reset the session to an unauthenticated, default state. """ self.__gmusic.logout() def set_play_mode(self, mode): """ Set the playback mode. :param mode: curren tvalid values are "NORMAL" and "SHUFFLE" """ self.current_play_mode = getattr(self.play_modes, mode) self.__update_play_queue_order() def current_song_title_and_artist(self): """ Retrieve the current track's title and artist name. """ logging.info("current_song_title_and_artist") song = self.now_playing_song if song is not None: title = to_ascii(self.now_playing_song.get('title')) artist = to_ascii(self.now_playing_song.get('artist')) logging.info("Now playing {0} by {1}".format(title, artist)) return artist, title else: return '', '' def current_song_album_and_duration(self): """ Retrieve the current track's album and duration. """ logging.info("current_song_album_and_duration") song = self.now_playing_song if song is not None: album = to_ascii(self.now_playing_song.get('album')) duration = to_ascii \ (self.now_playing_song.get('durationMillis')) logging.info("album {0} duration {1}".format(album, duration)) return album, int(duration) else: return '', 0 def current_track_and_album_total(self): """Return the current track number and the total number of tracks in the album, if known. """ logging.info("current_track_and_album_total") song = self.now_playing_song track = 0 total = 0 if song is not None: try: track = self.now_playing_song['trackNumber'] total = self.now_playing_song['totalTrackCount'] logging.info("track number {0} total tracks {1}" .format(track, total)) except KeyError: logging.info("trackNumber or totalTrackCount : not found") else: logging.info("current_song_track_number_" "and_total_tracks : not found") return track, total def current_song_year(self): """ Return the current track's year of publication. """ logging.info("current_song_year") song = self.now_playing_song year = 0 if song is not None: try: year = song['year'] logging.info("track year {0}".format(year)) except KeyError: logging.info("year : not found") else: logging.info("current_song_year : not found") return year def clear_queue(self): """ Clears the playback queue. """ self.queue = list() self.queue_index = -1 def enqueue_artist(self, arg): """ Search the user's library for tracks from the given artist and adds them to the playback queue. :param arg: an artist """ try: self.__update_local_library() artist = None if not arg in self.library.keys(): for name, art in self.library.iteritems(): if arg.lower() in name.lower(): artist = art print_wrn("[Google Play Music] '{0}' not found. " \ "Playing '{1}' instead." \ .format(arg, name)) break if not artist: raise KeyError("Artist not found : {0}".format(arg)) else: artist = self.library[arg] tracks_added = 0 for album in artist: tracks_added += self.__enqueue_tracks(artist[album]) logging.info("Added {0} tracks by {1} to queue" \ .format(tracks_added, arg)) self.__update_play_queue_order() except KeyError: raise KeyError("Artist not found : {0}".format(arg)) def enqueue_album(self, arg): """ Search the user's library for albums with a given name and adds them to the playback queue. """ try: self.__update_local_library() album = None artist = None tentative_album = None tentative_artist = None for library_artist in self.library: for artist_album in self.library[library_artist]: print_nfo("[Google Play Music] [Album] '{0}'." \ .format(to_ascii(artist_album))) if not album: if arg.lower() == artist_album.lower(): album = artist_album artist = library_artist break if not tentative_album: if arg.lower() in artist_album.lower(): tentative_album = artist_album tentative_artist = library_artist if album: break if not album: album = tentative_album artist = tentative_artist print_wrn("[Google Play Music] '{0}' not found. " \ "Playing '{1}' instead." \ .format(arg, album)) if not album: raise KeyError("Album not found : {0}".format(arg)) tracks_added = self.__enqueue_tracks(self.library[artist][album]) logging.info("Added {0} tracks from {1} by " \ "{2} to queue" \ .format(tracks_added, album, artist)) self.__update_play_queue_order() except KeyError: raise KeyError("Album not found : {0}".format(arg)) def enqueue_playlist(self, arg): """Search the user's library for playlists with a given name and adds the tracks of the first match to the playback queue. Requires Unlimited subscription. """ try: self.__update_local_library() self.__update_playlists() self.__update_playlists_unlimited() playlist = None for name, plist in self.playlists.items(): print_nfo("[Google Play Music] [Playlist] '{0}'." \ .format(to_ascii(name))) if not arg in self.playlists.keys(): for name, plist in self.playlists.iteritems(): if arg.lower() in name.lower(): playlist = plist print_wrn("[Google Play Music] '{0}' not found. " \ "Playing '{1}' instead." \ .format(arg, to_ascii(name))) break if not playlist: raise KeyError else: playlist = self.playlists[arg] tracks_added = self.__enqueue_tracks(playlist) logging.info("Added {0} tracks from {1} to queue" \ .format(tracks_added, arg)) self.__update_play_queue_order() except KeyError: raise KeyError("Playlist not found : {0}".format(arg)) def enqueue_station_unlimited(self, arg): """Search the user's library for a station with a given name and adds its tracks to the playback queue. Requires Unlimited subscription. """ try: # First try to find a suitable station in the user's library self.__enqueue_user_station_unlimited(arg) if not len(self.queue): # If no suitable station is found in the user's library, then # search google play unlimited for a potential match. self.__enqueue_station_unlimited(arg) if not len(self.queue): raise KeyError except KeyError: raise KeyError("Station not found : {0}".format(arg)) def enqueue_genre_unlimited(self, arg): """Search Unlimited for a genre with a given name and adds its tracks to the playback queue. Requires Unlimited subscription. """ print_msg("[Google Play Music] [Retrieving genres] : '{0}'. " \ .format(self.__email)) try: all_genres = list() root_genres = self.__gmusic.get_genres() second_tier_genres = list() for root_genre in root_genres: second_tier_genres += self.__gmusic.get_genres(root_genre['id']) all_genres += root_genres all_genres += second_tier_genres for genre in all_genres: print_nfo("[Google Play Music] [Genre] '{0}'." \ .format(to_ascii(genre['name']))) genre = dict() if not arg in all_genres: genre = next((g for g in all_genres \ if arg.lower() in to_ascii(g['name']).lower()), \ None) if genre: print_wrn("[Google Play Music] '{0}' not found. " \ "Playing '{1}' instead." \ .format(arg, to_ascii(genre['name']))) else: raise KeyError("Genre not found : {0}".format(arg)) genre_name = genre['name'] genre_id = genre['id'] station_id = self.__gmusic.create_station(genre_name, \ None, None, None, genre_id) num_tracks = 200 tracks = self.__gmusic.get_station_tracks(station_id, num_tracks) tracks_added = self.__enqueue_tracks(tracks) logging.info("Added {0} tracks from {1} to queue" \ .format(tracks_added, genre_name)) self.__update_play_queue_order() except KeyError: raise KeyError("Genre not found : {0}".format(arg)) except CallFailure: raise RuntimeError("Operation requires an Unlimited subscription.") def enqueue_artist_unlimited(self, arg): """Search Unlimited for an artist and adds the artist's 200 top tracks to the playback queue. Requires Unlimited subscription. """ try: artist_hits = self.__gmusic.search_all_access(arg)['artist_hits'] artist = next((hit for hit in artist_hits \ if 'best_result' in hit.keys()), None) if not artist and len(artist_hits): for hit in artist_hits: print_nfo("[Google Play Music] [Artist] '{0}'." \ .format((hit['artist']['name']).encode('utf-8'))) artist = artist_hits[0] print_wrn("[Google Play Music] Playing '{0}'." \ .format((artist['artist']['name']).encode('utf-8'))) include_albums = False max_top_tracks = 200 max_rel_artist = 0 artist_tracks = dict() if artist: artist_tracks = self.__gmusic.get_artist_info \ (artist['artist']['artistId'], include_albums, max_top_tracks, max_rel_artist)['topTracks'] if not artist_tracks: raise KeyError tracks_added = self.__enqueue_tracks(artist_tracks) logging.info("Added {0} tracks from {1} to queue" \ .format(tracks_added, arg)) self.__update_play_queue_order() except KeyError: raise KeyError("Artist not found : {0}".format(arg)) except CallFailure: raise RuntimeError("Operation requires an Unlimited subscription.") def enqueue_album_unlimited(self, arg): """Search Unlimited for an album and adds its tracks to the playback queue. Requires Unlimited subscription. """ try: album_hits = self.__gmusic.search_all_access(arg)['album_hits'] album = next((hit for hit in album_hits \ if 'best_result' in hit.keys()), None) if not album and len(album_hits): for hit in album_hits: print_nfo("[Google Play Music] [Album] '{0}'." \ .format((hit['album']['name']).encode('utf-8'))) album = album_hits[0] print_wrn("[Google Play Music] Playing '{0}'." \ .format((album['album']['name']).encode('utf-8'))) album_tracks = dict() if album: album_tracks = self.__gmusic.get_album_info \ (album['album']['albumId'])['tracks'] if not album_tracks: raise KeyError tracks_added = self.__enqueue_tracks(album_tracks) logging.info("Added {0} tracks from {1} to queue" \ .format(tracks_added, arg)) self.__update_play_queue_order() except KeyError: raise KeyError("Album not found : {0}".format(arg)) except CallFailure: raise RuntimeError("Operation requires an Unlimited subscription.") def enqueue_tracks_unlimited(self, arg): """ Search Unlimited for a track name and adds all the matching tracks to the playback queue. Requires Unlimited subscription. """ print_msg("[Google Play Music] [Retrieving library] : '{0}'. " \ .format(self.__email)) try: track_hits = self.__gmusic.search_all_access(arg)['song_hits'] tracks = list() for hit in track_hits: tracks.append(hit['track']) tracks_added = self.__enqueue_tracks(tracks) logging.info("Added {0} tracks from {1} to queue" \ .format(tracks_added, arg)) self.__update_play_queue_order() except KeyError: raise KeyError("Playlist not found : {0}".format(arg)) except CallFailure: raise RuntimeError("Operation requires an Unlimited subscription.") def enqueue_promoted_tracks_unlimited(self): """ Retrieve the url of the next track in the playback queue. """ try: tracks = self.__gmusic.get_promoted_songs() count = 0 for track in tracks: store_track = self.__gmusic.get_track_info(track['storeId']) if not u'id' in store_track.keys(): store_track[u'id'] = store_track['nid'] self.queue.append(store_track) count += 1 if count == 0: print_wrn("[Google Play Music] Operation requires " \ "an Unlimited subscription.") logging.info("Added {0} Unlimited promoted tracks to queue" \ .format(count)) self.__update_play_queue_order() except CallFailure: raise RuntimeError("Operation requires an Unlimited subscription.") def next_url(self): """ Retrieve the url of the next track in the playback queue. """ if len(self.queue): self.queue_index += 1 if (self.queue_index < len(self.queue)) \ and (self.queue_index >= 0): next_song = self.queue[self.play_queue_order[self.queue_index]] return self.__retrieve_track_url(next_song) else: self.queue_index = -1 return self.next_url() else: return '' def prev_url(self): """ Retrieve the url of the previous track in the playback queue. """ if len(self.queue): self.queue_index -= 1 if (self.queue_index < len(self.queue)) \ and (self.queue_index >= 0): prev_song = self.queue[self.play_queue_order[self.queue_index]] return self.__retrieve_track_url(prev_song) else: self.queue_index = len(self.queue) return self.prev_url() else: return '' def __update_play_queue_order(self): """ Update the queue playback order. A sequential order is applied if the current play mode is "NORMAL" or a random order if current play mode is "SHUFFLE" """ total_tracks = len(self.queue) if total_tracks: if not len(self.play_queue_order): # Create a sequential play order, if empty self.play_queue_order = range(total_tracks) if self.current_play_mode == self.play_modes.SHUFFLE: random.shuffle(self.play_queue_order) print_nfo("[Google Play Music] [Tracks in queue] '{0}'." \ .format(total_tracks)) def __retrieve_track_url(self, song): """ Retrieve a song url """ logging.info("__retrieve_track_url : {0}".format(song['id'])) song_url = self.__gmusic.get_stream_url(song['id'], self.__device_id) try: self.now_playing_song = song return song_url except AttributeError: logging.info("Could not retrieve the song url!") raise def __update_local_library(self): """ Retrieve the songs and albums from the user's library """ print_msg("[Google Play Music] [Retrieving library] : '{0}'. " \ .format(self.__email)) songs = self.__gmusic.get_all_songs() self.playlists[self.thumbs_up_playlist_name] = list() # Retrieve the user's song library for song in songs: if "rating" in song and song['rating'] == "5": self.playlists[self.thumbs_up_playlist_name].append(song) song_id = song['id'] song_artist = song['artist'] song_album = song['album'] self.song_map[song_id] = song if song_artist == "": song_artist = "Unknown Artist" if song_album == "": song_album = "Unknown Album" if not song_artist in self.library: self.library[song_artist] = CaseInsensitiveDict() self.library[song_artist][self.all_songs_album_title] = list() if not song_album in self.library[song_artist]: self.library[song_artist][song_album] = list() self.library[song_artist][song_album].append(song) self.library[song_artist][self.all_songs_album_title].append(song) # Sort albums by track number for artist in self.library.keys(): logging.info("Artist : {0}".format(to_ascii(artist))) for album in self.library[artist].keys(): logging.info(" Album : {0}".format(to_ascii(album))) if album == self.all_songs_album_title: sorted_album = sorted(self.library[artist][album], key=lambda k: k['title']) else: sorted_album = sorted(self.library[artist][album], key=lambda k: k.get('trackNumber', 0)) self.library[artist][album] = sorted_album def __update_stations_unlimited(self): """ Retrieve stations (Unlimited) """ self.stations.clear() stations = self.__gmusic.get_all_stations() self.stations[u"I'm Feeling Lucky"] = 'IFL' for station in stations: station_name = station['name'] logging.info("station name : {0}" \ .format(to_ascii(station_name))) self.stations[station_name] = station['id'] def __enqueue_user_station_unlimited(self, arg): """ Enqueue a user station (Unlimited) """ print_msg("[Google Play Music] [Station search "\ "in user's library] : '{0}'. " \ .format(self.__email)) self.__update_stations_unlimited() station_name = arg station_id = None for name, st_id in self.stations.iteritems(): print_nfo("[Google Play Music] [Station] '{0}'." \ .format(to_ascii(name))) if not arg in self.stations.keys(): for name, st_id in self.stations.iteritems(): if arg.lower() in name.lower(): station_id = st_id station_name = name break else: station_id = self.stations[arg] num_tracks = 200 tracks = dict() if station_id: try: tracks = self.__gmusic.get_station_tracks(station_id, \ num_tracks) except KeyError: raise RuntimeError("Operation requires an " "Unlimited subscription.") tracks_added = self.__enqueue_tracks(tracks) if tracks_added: if arg != station_name: print_wrn("[Google Play Music] '{0}' not found. " \ "Playing '{1}' instead." \ .format(arg.encode('utf-8'), name.encode('utf-8'))) logging.info("Added {0} tracks from {1} to queue" \ .format(tracks_added, arg)) self.__update_play_queue_order() else: print_wrn("[Google Play Music] '{0}' has no tracks. " \ .format(station_name)) if not len(self.queue): print_wrn("[Google Play Music] '{0}' unable to find a " \ "suitable station in user's library. " \ .format(arg)) def __enqueue_station_unlimited(self, arg): """Search for a station and enqueue all of its tracks (Unlimited) """ print_msg("[Google Play Music] [Station search in "\ "Google Play Music Unlimited] : '{0}'. " \ .format(arg)) try: station_name = arg station_id = None station_hits = self.__gmusic.search_all_access(arg)['station_hits'] station = next((hit for hit in station_hits \ if 'best_result' in hit.keys()), None) if not station and len(station_hits): for hit in station_hits: print_nfo("[Google Play Music] [Station] '{0}'." \ .format((hit['station']['name']).encode('utf-8'))) if not station: if arg.lower() in \ to_ascii(hit['station']['name']).lower(): station = hit if not station: # Play some random station from the search results random.seed() station_index = random.randint(0, len(station_hits)) station = station_hits[station_index] if station: station = station['station'] station_name = station['name'] seed = station['seed'] seed_type = seed['seedType'] track_id = seed['trackId'] if seed_type == u'2' else None artist_id = seed['artistId'] if seed_type == u'3' else None album_id = seed['albumId'] if seed_type == u'4' else None genre_id = seed['genreId'] if seed_type == u'5' else None num_tracks = 200 tracks = dict() try: station_id \ = self.__gmusic.create_station(station_name, \ track_id, \ artist_id, \ album_id, \ genre_id) tracks \ = self.__gmusic.get_station_tracks(station_id, \ num_tracks) except KeyError: raise RuntimeError("Operation requires an " "Unlimited subscription.") tracks_added = self.__enqueue_tracks(tracks) if tracks_added: print_wrn("[Google Play Music] [Station] : '{0}'." \ .format(station_name.encode('utf-8'))) logging.info("Added {0} tracks from {1} to queue" \ .format(tracks_added, arg)) self.__update_play_queue_order() except KeyError: raise KeyError("Station not found : {0}".format(arg)) def __enqueue_tracks(self, tracks): """ Add tracks to the playback queue """ count = 0 for track in tracks: if not u'id' in track.keys(): track[u'id'] = track['nid'] self.queue.append(track) count += 1 return count def __update_playlists(self): """ Retrieve the user's playlists """ plists = self.__gmusic.get_all_user_playlist_contents() for plist in plists: plist_name = plist['name'] logging.info("playlist name : {0}".format(to_ascii(plist_name))) tracks = plist['tracks'] tracks.sort(key=itemgetter('creationTimestamp')) self.playlists[plist_name] = list() for track in tracks: try: song = self.song_map[track['trackId']] self.playlists[plist_name].append(song) except IndexError: pass def __update_playlists_unlimited(self): """ Retrieve shared playlists (Unlimited) """ plists_subscribed_to = [p for p in self.__gmusic.get_all_playlists() \ if p.get('type') == 'SHARED'] for plist in plists_subscribed_to: share_tok = plist['shareToken'] playlist_items \ = self.__gmusic.get_shared_playlist_contents(share_tok) plist_name = plist['name'] logging.info("shared playlist name : {0}" \ .format(to_ascii(plist_name))) self.playlists[plist_name] = list() for item in playlist_items: try: song = item['track'] song['id'] = item['trackId'] self.playlists[plist_name].append(song) except IndexError: pass
from gmusicapi import Mobileclient from pprint import pprint api = Mobileclient() api.login('', '', Mobileclient.FROM_MAC_ADDRESS) playlist_name = 'ToListenTo' answer = api.search_all_access('', max_results=1) sweet_track_ids = [] artist_id = answer['artist_hits'][0]['artist']['artistId'] response = api.get_artist_info(artist_id, include_albums=False, max_top_tracks=3, max_rel_artist=0) for song in response['topTracks']: sweet_track_ids.append(song['nid']) playlists = api.get_all_playlists() playlist_id = None for playlist in playlists: if playlist_name in playlist['name']: playlist_id = playlist['id'] if not playlist_id: playlist_id = api.create_playlist(playlist_name) api.add_songs_to_playlist(playlist_id, sweet_track_ids)
class GMusic(object): def __init__(self): self.authenticated = False self.all_access = False self.library_loaded = False self.all_songs = [] self.letters = {} self.artists = {} self.albums = {} self.genres = {} self.tracks_by_letter = {} self.tracks_by_artist = {} self.tracks_by_album = {} self.tracks_by_genre = {} self._device = None self._webclient = Webclient(debug_logging=False) self._mobileclient = Mobileclient(debug_logging=False) self._playlists = [] self._playlist_contents = [] self._stations = [] def _get_device_id(self): if self.authenticated: devices = self._webclient.get_registered_devices() for dev in devices: if dev['type'] == 'PHONE': self._device = dev['id'][2:] break elif dev['type'] == 'IOS': self._device = dev['id'] break def _set_all_access(self): settings = self._webclient._make_call(webclient.GetSettings, '') self.all_access = True if 'isSubscription' in settings[ 'settings'] and settings['settings'][ 'isSubscription'] == True else False def _set_all_songs(self): if len(self.all_songs) == 0: try: self.all_songs = self._mobileclient.get_all_songs() except NotLoggedIn: if self.authenticate(): self.all_songs = self._mobileclient.get_all_songs() else: return [] else: return self.all_songs def authenticate(self, email, password): try: mcauthenticated = self._mobileclient.login(email, password) except AlreadyLoggedIn: mcauthenticated = True try: wcauthenticated = self._webclient.login(email, password) except AlreadyLoggedIn: wcauthenticated = True self.authenticated = mcauthenticated and wcauthenticated self._set_all_access() self._get_device_id() return self.authenticated def load_data(self): self._set_all_songs() for song in self.all_songs: thumb = None letter = song['title'][0] artist = song['artist'] album = song['album'] genre = song['genre'] if 'genre' in song else '(None)' if letter not in self.tracks_by_letter: self.tracks_by_letter[letter] = [] self.letters[letter] = None if artist not in self.tracks_by_artist: self.tracks_by_artist[artist] = [] self.artists[artist] = None if album not in self.tracks_by_album: self.tracks_by_album[album] = [] self.albums[album] = None if genre not in self.tracks_by_genre: self.tracks_by_genre[genre] = [] self.genres[genre] = None track = {'artist': artist, 'album': album} if 'title' in song: track['title'] = song['title'] if 'album' in song: track['album'] = song['album'] if 'artist' in song: track['artist'] = song['artist'] if 'durationMillis' in song: track['durationMillis'] = song['durationMillis'] if 'id' in song: track['id'] = song['id'] if 'trackNumber' in song: track['trackType'] = song['trackNumber'] if 'storeId' in song: track['storeId'] = song['storeId'] if 'albumArtRef' in song: track['albumArtRef'] = song['albumArtRef'] thumb = song['albumArtRef'][0]['url'] self.letters[letter] = thumb self.artists[artist] = thumb self.albums[album] = thumb self.genres[genre] = thumb self.tracks_by_letter[letter].append({ 'track': track, 'thumb': thumb, 'id': song['id'] }) self.tracks_by_artist[artist].append({ 'track': track, 'thumb': thumb, 'id': song['id'] }) self.tracks_by_album[album].append({ 'track': track, 'thumb': thumb, 'id': song['id'] }) self.tracks_by_genre[genre].append({ 'track': track, 'thumb': thumb, 'id': song['id'] }) self.library_loaded = True def get_tracks_for_type(self, type, name): type = type.lower() if type == 'artists': return self.tracks_by_artist[name] elif type == 'albums': return self.tracks_by_album[name] elif type == 'genres': return self.tracks_by_genre[name] elif type == 'songs by letter': return self.tracks_by_letter[name] else: return {} def get_song(self, id): return [x for x in self.all_songs if x['id'] == id][0] def get_all_playlists(self): if len(self._playlists) == 0: try: self._playlists = self._mobileclient.get_all_playlists() except NotLoggedIn: if self.authenticate(): self._playlists = self._mobileclient.get_all_playlists() else: return [] return self._playlists def get_all_user_playlist_contents(self, id): tracks = [] if len(self._playlist_contents) == 0: try: self._playlist_contents = self._mobileclient.get_all_user_playlist_contents( ) except NotLoggedIn: if self.authenticate(): self._playlist_contents = self._mobileclient.get_all_user_playlist_contents( ) else: return [] for playlist in self._playlist_contents: if id == playlist['id']: tracks = playlist['tracks'] break return tracks def get_shared_playlist_contents(self, token): playlist = [] try: playlist = self._mobileclient.get_shared_playlist_contents(token) except NotLoggedIn: if self.authenticate(): playlist = self._mobileclient.get_shared_playlist_contents( token) else: return [] return playlist def get_all_stations(self): if len(self._stations) == 0: try: self._stations = self._mobileclient.get_all_stations() except NotLoggedIn: if self.authenticate(): self._stations = self._mobileclient.get_all_stations() else: return [] return self._stations def get_station_tracks(self, id, num_tracks=200): tracks = [] try: tracks = self._mobileclient.get_station_tracks(id, num_tracks) except NotLoggedIn: if self.authenticate(): tracks = self._mobileclient.get_station_tracks(id, num_tracks) else: return [] return tracks def get_genres(self): genres = [] try: genres = self._mobileclient.get_genres() except NotLoggedIn: if self.authenticate(): genres = self._mobileclient.get_genres() else: return [] return genres def create_station(self, name, id): station = None try: station = self._mobileclient.create_station(name=name, genre_id=id) except NotLoggedIn: if self.authenticate(): station = self._mobileclient.create_station(name=name, genre_id=id) else: return [] return station def search_all_access(self, query, max_results=50): results = None try: results = self._mobileclient.search_all_access(query, max_results) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.search_all_access( query, max_results) else: return [] return results def get_artist_info(self, id, include_albums=True, max_top_tracks=5, max_rel_artist=5): results = None try: results = self._mobileclient.get_artist_info( id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.get_artist_info( id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist) else: return [] return results def get_album_info(self, id, include_tracks=True): results = None try: results = self._mobileclient.get_album_info( id, include_tracks=include_tracks) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.get_album_info( id, include_tracks=include_tracks) else: return [] return results def add_aa_track(self, id): track = None try: track = self._mobileclient.add_aa_track(id) except NotLoggedIn: if self.authenticate(): track = self._mobileclient.add_aa_track(id) else: return None return track def add_songs_to_playlist(self, playlist_id, song_ids): tracks = None try: tracks = self._mobileclient.add_songs_to_playlist( playlist_id, song_ids) except NotLoggedIn: if self.authenticate(): tracks = self._mobileclient.add_songs_to_playlist( playlist_id, song_ids) else: return None return tracks def get_stream_url(self, id): try: stream_url = self._mobileclient.get_stream_url(id, self._device) except NotLoggedIn: if self.authenticate(): stream_url = self._mobileclient.get_stream_url( id, self._device) else: return '' except CallFailure: raise CallFailure('Could not play song with id: ' + id, 'get_stream_url') return stream_url
count = min(len(table), args.li) else: count = len(table) if logged_in: playLists = api.get_all_user_playlist_contents() playlist = next(p for p in playLists if p['name'] == args.pl) for i in range(0, count): column = table[i].xpath('td') """SomaFM prints breaks and in the schedule so we only want rows in the table that are a track""" if len(column) == 5: song.artist = column[1].xpath('a')[0].text song.songName = column[2].text song.album = column[3].xpath('a')[0].text result = api.search_all_access(song.artist + " " + song.songName, 1) if result['song_hits']: songInPlaylist = False for track in playlist['tracks']: if track['trackId'] == result['song_hits'][0]['track']['nid']: print 'playlist already contains ', song.songName songInPlaylist = True if not songInPlaylist: print 'playlist dose not already contain ', song.songName api.add_songs_to_playlist(playlist['id'], result['song_hits'][0]['track']['nid'])
"you want imported. If none of them properly match, just type in 'none'." matchHistory = {} for k in playlist_map: print '**********************************' print 'now importing ' + k print '**********************************' newPID = api.create_playlist(k) playlist_songs = playlist_map[k] for song in playlist_songs: special_char = song.find('\\') while special_char >= 0: song = song[:special_char] + song[special_char + 1:] special_char = song.find('\\') sTokens = song.split('`') song, duration = sTokens[0], int(sTokens[1]) results = api.search_all_access(song, 10)['song_hits'] song_id = findMatch(results, song, duration, explicit_mode, matchHistory) if song_id is None: results = api.search_all_access(song.split('~')[0], 10)['song_hits'] song_id = findMatch(results, song, duration, explicit_mode, matchHistory) if song_id is not None: if errcount >= 20: raise Exception( 'Too many errors have occurred - the server is not responding properly.' ) while errcount < 20: try: api.add_songs_to_playlist(newPID, song_id)
logged_in = mobile_client.login(email, password) # logged_in is True if login was successful if logged_in == True: print "Successfully logged in" if device_id == '': devices = web_client.get_registered_devices() valid = [device['id'][2:] + " (" + device['model'] + ")" for device in devices if device['type'] == 'PHONE'] print valid id_index = int(raw_input("Device ID: ")) device_id = valid[id_index].split(' ', 1)[0] while True: song_name = raw_input("Song title: ") results = mobile_client.search_all_access(song_name, 1) song = results['song_hits'][0] song_id = song['track']['nid'] song_name = song['track']['title'] song_artist = song['track']['artist'] stream_url = mobile_client.get_stream_url(song_id, device_id) # Uncomment to save most recent song # urllib.urlretrieve (stream_url, "mp3.mp3") print "Now Playing " + song_name + " by " + song_artist p.set_mrl(stream_url) p.play() else: print "Invalid login credintials"
from gmusicapi import Mobileclient from pprint import pprint api = Mobileclient() api.login('', '', Mobileclient.FROM_MAC_ADDRESS) playlist_name = 'ToListenTo' answer=api.search_all_access('', max_results=1) sweet_track_ids = [] artist_id=answer['artist_hits'][0]['artist']['artistId'] response = api.get_artist_info(artist_id, include_albums=False, max_top_tracks=3, max_rel_artist=0) for song in response['topTracks']: sweet_track_ids.append(song['nid']) playlists = api.get_all_playlists() playlist_id = None for playlist in playlists: if playlist_name in playlist['name']: playlist_id = playlist['id'] if not playlist_id: playlist_id = api.create_playlist(playlist_name) api.add_songs_to_playlist(playlist_id, sweet_track_ids)
class GoogleMusic(object): def __init__(self): self.webclient = Webclient() self.mobileclient = Mobileclient() def is_authenticated(self): if self.webclient.is_authenticated(): if self.mobileclient.is_authenticated(): return True return False def login(self, username, password): if not self.is_authenticated(): try: self.mobileclient.login(username, password) self.webclient.login(username, password) except: raise Exception('Couldn\'t log into Google Music') def search(self, query, kind): if self.is_authenticated(): results = self.mobileclient.search_all_access(query)[kind + '_hits'] return results def get_track(self, store_id): return self.mobileclient.get_track_info(store_id) def save_stream(self, track, destination): if self.is_authenticated(): with open(destination, 'w+b') as stream_file: urls = self.webclient.get_stream_urls(track.get('storeId')) if len(urls) == 1: stream_file.write( self.webclient.session._rsession.get(urls[0]).content) 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'] for url, (start, end) in zip(urls, range_pairs): stream_file.truncate(start) stream_file.seek(0, 2) audio = self.webclient.session._rsession.get(url).content stream_file.write(audio) tag = easyid3.EasyID3() tag['title'] = track.get('title').__str__() tag['artist'] = track.get('artist').__str__() tag['album'] = track.get('album').__str__() tag['date'] = track.get('year').__str__() tag['discnumber'] = track.get('discNumber').__str__() tag['tracknumber'] = track.get('trackNumber').__str__() tag['performer'] = track.get('albumArtist').__str__() tag.save(destination) tag = mp3.MP3(destination) tag.tags.add( id3.APIC( 3, 'image/jpeg', 3, 'Front cover', urllib.urlopen( track.get('albumArtRef')[0].get('url')).read())) tag.save()
else: track_name = link['track_name'] q = link['artist_name'] + ' ' + track_name queries.append(q) #Now Google mc = Mobileclient() mc.login(config['email'], config['password'], config['android_device_id']) hits = 0 misses = 0 track_ids = [] failed_queries = [] for q in queries: search = mc.search_all_access(q, max_results=5) g_songs = search['song_hits'] if any(g_songs): sort_by_score = sorted(g_songs, key=itemgetter('score'), reverse=True) #print sort_by_score[0]['track']['storeId'] track_ids.append(sort_by_score[0]['track']['storeId']) hits += 1 else: misses += 1 failed_queries.append(q) print 'Hits: {0} Misses: {1}'.format(hits,misses) if misses > 0: print 'Unable to find a Google Music match for these tracks:' print failed_queries
from gmusicapi import Mobileclient api = Mobileclient() api.login("*****@*****.**", "osilgdgrnmiigtfz", Mobileclient.FROM_MAC_ADDRESS) print(api.search_all_access("Hello"))