class GoogleMusic(Source): def __init__(self, library, username, password): Source.__init__(self, library, SourceType.GOOGLE) self.GOOGLE_DEVICE_ID = None print username print password self.client = Mobileclient() logged_in = self.client.login(username, password, Mobileclient.FROM_MAC_ADDRESS) print "Google logged in:", logged_in DList = self.client.get_registered_devices() self.GOOGLE_DEVICE_ID = DList[0]["id"] if self.GOOGLE_DEVICE_ID[:2] == '0x': self.GOOGLE_DEVICE_ID = self.GOOGLE_DEVICE_ID[2:] print self.GOOGLE_DEVICE_ID #>testing # self.get_stream_URL("47b9d52c-9d66-3ff2-94d4-3ae55c0d2acc") def get_stream_URL(self, song_id): return self.client.get_stream_url(song_id, self.GOOGLE_DEVICE_ID) def sync(self): gmusic_tracks = self.client.get_all_songs() for track in gmusic_tracks: art = '' try: art = track['albumArtRef'][0]['url'] except KeyError: art = '' self.library.insert_track(track['title'], track['album'], track['artist'], self._source, str(track['id']), track['trackNumber'], art)
def search(self, lib, opts, args): password = config['gmusic']['password'] email = config['gmusic']['email'] password.redact = True email.redact = True # Since Musicmanager doesn't support library management # we need to use mobileclient interface mobile = Mobileclient() try: mobile.login(email.as_str(), password.as_str(), Mobileclient.FROM_MAC_ADDRESS) files = mobile.get_all_songs() except NotLoggedIn: ui.print_( u'Authentication error. Please check your email and password.' ) return if not args: for i, file in enumerate(files, start=1): print(i, ui.colorize('blue', file['artist']), file['title'], ui.colorize('red', file['album'])) else: if opts.track: self.match(files, args, 'title') else: self.match(files, args, 'artist')
def search(self, lib, opts, args): password = config['gmusic']['password'] email = config['gmusic']['email'] password.redact = True email.redact = True # Since Musicmanager doesn't support library management # we need to use mobileclient interface mobile = Mobileclient() try: mobile.login(email.as_str(), password.as_str(), Mobileclient.FROM_MAC_ADDRESS) files = mobile.get_all_songs() except NotLoggedIn: ui.print_( u'Authentication error. Please check your email and password.') return if not args: for i, file in enumerate(files, start=1): print(i, ui.colorize('blue', file['artist']), file['title'], ui.colorize('red', file['album'])) else: if opts.track: self.match(files, args, 'title') else: self.match(files, args, 'artist')
def get_data(self): mobileapi = Mobileclient() mobileapi.login(setting.GUSER, setting.GPASS) library = mobileapi.get_all_songs() mobileapi.logout() return library
def gm_get_current_pl_member(client: Mobileclient, playlist: str) -> (str, list): playlist = list(filter( lambda x: x['deleted'] == False and x['name'] == playlist, client.get_all_user_playlist_contents() ))[0] member_track_ids = set([item['trackId'] for item in playlist['tracks']]) return playlist['id'], [item for item in client.get_all_songs() if item['id'] in member_track_ids]
def google_music(gmail, psswd,last_sync = 0): from gmusicapi import Mobileclient api = Mobileclient() songs =[] if api.login(gmail, psswd, ): library = api.get_all_songs() for track in library: song = {} song['name'] = track.get('title','') song['artist'] = track.get('artist','') song['year'] = track.get('year',0) song['genre'] = track.get('genre','') song['plays'] = track.get('playCount',0) tup = youtubeVidSearch(song['name'],song['artist']) song['url'] = tup[0] song['art'] = tup[1] stamp = int(track.get('creationTimestamp',0))/1000.0 if stamp > last_sync and song.get('url') : songs.append(song) print track return songs
def update_stats(user_id): gm = Mobileclient() user = UserSettings.objects.get(pk=user_id) try: user_credential_base64 = user.credential user_credential = codecs.decode(user_credential_base64.encode(), "base64") credential = pickle.loads(user_credential) # TODO think about google apis problem gm.oauth_login(user.current_device, credential) try: library = gm.get_all_songs() gm.logout() except Exception as e: print("Exception: " + str(e)) return new_library = prepare_library(library) new_stats = PlayMusicStats() new_stats.user = user.user new_stats.stats = new_library new_stats.total_time = get_total_time_in_sec(new_library) new_stats.save() print('Stats for ' + str(user.user) + ' saved') except Exception as e: user.credential_is_valid = False user.save() print('Credential for ' + str(user.user) + ' is invalid: ' + str(e))
def get_songs(sync=False): if os.path.isfile('songs.json') and not sync: f = open('songs.json', 'r') songs = f.read() songs = json.loads(songs) else: api = Mobileclient() api.login(GOOGLE_EMAIL, GOOGLE_MUSIC_PASS, Mobileclient.FROM_MAC_ADDRESS) songs_gen = api.get_all_songs(True) print("Loading library songs:") songs = [] for part in songs_gen: songs = songs + part print("%s songs loaded" % len(songs)) str(len(songs)) + " songs loaded." songs = list(reversed(resort_by_added(songs))) f = open('songs.json', 'w') f.write(json.dumps(songs, indent=4, separators=(',', ': '))) f.close() return songs
class GMusicClient: def __init__(self): """ This connects to the google music server by requesting credentials. """ self.api = Mobileclient() # username = input('Type your Google Play Music email below.\n--> ') self.username = os.getenv('GOOGLE_USERNAME') dir_path = os.path.dirname(os.path.realpath(__file__)) + '/.cache-gmusic-' + ''.join(filter(str.isalpha, self.username)) # Check if already authenticated if(not os.path.isfile(dir_path)): self.api.perform_oauth(open_browser=True, storage_filepath=dir_path) # Attempt to log in; if fail, get new token. try: self.api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, oauth_credentials=dir_path) except: self.api.perform_oauth(open_browser=True, storage_filepath=dir_path) self.api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, oauth_credentials=dir_path) print('Connected to GMusic') def get_playlists(self): """ Gets all the playlists in Google Play Music. Some may not actually have any music, but they will be processed anyways. """ playlists_cache_path = os.path.dirname(os.path.realpath(__file__)) + '/.cache-playlists_cache-' + ''.join(filter(str.isalpha, self.username)) if (os.path.isfile(playlists_cache_path)): with open(playlists_cache_path, 'rb') as playlists_cache_file: playlists = pickle.load(playlists_cache_file) else: print('Requesting Google playlists') playlistsG = self.api.get_all_user_playlist_contents() print('Received Google playlists, we have', len(playlistsG), 'playlists') playlists = Playlists(playlistsG) with open(playlists_cache_path, 'wb') as playlists_cache_file: pickle.dump(playlists, playlists_cache_file) return playlists def get_all_songs(self): """ Gets the entire Google library for adding to the """ lib_cache_path = os.path.dirname(os.path.realpath(__file__)) + '/.cache-lib_cache-' + ''.join(filter(str.isalpha, self.username)) if (os.path.isfile(lib_cache_path)): with open(lib_cache_path, 'rb') as lib_cache_file: library = pickle.load(lib_cache_file) else: print('Requesting Google library') librarySongs = self.api.get_all_songs() print('Received Google library, we have', len(librarySongs), 'songs') library = MusicLibrary(librarySongs) with open(lib_cache_path, 'wb') as lib_cache_file: pickle.dump(library, lib_cache_file) return library
def get_google_songs(mc: Mobileclient, mintimestamp: int = 0): arr = mc.get_all_songs() filterd = [] mintimestamp *= 1000 for elem in arr: if int(elem["creationTimestamp"]) > mintimestamp: filterd.append(elem["id"]) return filterd
class GoogleMusicHelper(object): def __init__(self, email=None, password=None): self.google_music_client = Mobileclient() if email and password: self.login(email, password) def login(self, email, password): if self.google_music_client.login(email, password, Mobileclient.FROM_MAC_ADDRESS): return "Logged in to Google" return "Error logging in" def add_song_by_name_to_google_library(self, song="", artist=""): results = self.google_music_client.search(query=song + " " + artist, max_results=1) if results: track = results["song_hits"][0]["track"] return self.google_music_client.add_store_tracks( track.get("storeId") or track.get("nid")) def list_playlists(self): return self.google_music_client.get_all_user_playlist_contents() def sync_playlists_with_library(self, password=None, username=None): if self.google_music_client.login(username, password, Mobileclient.FROM_MAC_ADDRESS): all_tracks = [] for playlist in self.google_music_client.get_all_user_playlist_contents( ): for track in playlist["tracks"]: all_tracks.append(track["track"]) playlist_store_ids = [track["storeId"] for track in all_tracks] all_songs = self.google_music_client.get_all_songs( incremental=False) print all_songs[0] added_store_ids = [] for song in all_songs: store_id = None if song.get("nid"): store_id = song["nid"] elif song.get("storeId"): store_id = song["storeId"] added_store_ids.append(store_id) new_store_ids = set(playlist_store_ids) - set(added_store_ids) new_tracks = [ track for track in all_tracks if track["storeId"] not in added_store_ids ] for storeId in new_store_ids: for track in new_tracks: if track["storeId"] == storeId: break print track['title'] + " by " + track["artist"] print self.google_music_client.add_store_tracks(storeId)
class Plugin: name = 'gmusic' def __init__(self, username, password): self.client = Mobileclient() self.client.login(username, password, Mobileclient.FROM_MAC_ADDRESS) # self.webclient = Webclient() # self.webclient.login(username, password) def get_tracks(self, artist=None, album=None): """ Fetches tracks from api. If no filter is defined, it will get user tracks """ return TrackList(self.client.get_all_songs()) def get_playlists(self): """ Get playlists and radios """ playlists = [] for playlist in self.client.get_all_user_playlist_contents(): tracks = TrackList([ self.client.get_track_info(x['trackId']) for x in playlist['tracks'] ]) playlists.append(PlayList(playlist['name'], tracks)) return playlists def stream(self, track): def _stream(url): inp = requests.get(url, stream=True) chunk_size = 1024 for chunk in inp.iter_content(chunk_size): if not chunk: continue yield chunk song_id = track.uri.split(':')[-1] return _stream(self.client.get_stream_url(song_id)) def search(self, keywords, matches): results = self.client.search(keywords) if matches == 'artist': return {'artists': results.get('artist_hits', [])} elif matches == 'album': return {'albums': results.get('album_hits', [])} elif matches == 'tracks': return {'tracks': results.get('song_hits', [])} elif matches == 'all': return { 'artists': results.get('artist_hits', []), 'albums': results.get('album_hits', []), 'tracks': results.get('song_hits', []) }
def download_album(username, password, artist_name, album_name): api = Mobileclient() api.login(username, password, Mobileclient.FROM_MAC_ADDRESS) library = api.get_all_songs() songs = [s for s in library if s['albumArtist'] == artist_name and s['album'] == album_name] if len(songs) == 0: print('Error: Album not found', file=sys.stderr) return device_id = api.get_registered_devices()[0]['id'].replace('0x', '') dname = slugify(unicode(album_name)) os.mkdir(dname) # download songs for song in tqdm(songs, desc='Downloading'): fname = slugify(song['title']) mpg_name = os.path.join(dname, fname + '.mpg') mp3_name = os.path.join(dname, fname + '.mp3') url = api.get_stream_url(song['id'], device_id=device_id) response = requests.get(url) # first save as MPEG video with open(mpg_name, 'wb') as fout: for chunk in response.iter_content(chunk_size=128): fout.write(chunk) # call FFMPEG to convert to MP3 os.system(' '.join([FFMPEG_CMD] + FFMPEG_ARGS).format( input=mpg_name, output=mp3_name, title=song['title'], artist=song['albumArtist'], album=song['album'], track=song['trackNumber'])) os.remove(mpg_name) # download album art art_name = os.path.join(dname, dname + '.png') album_info = api.get_album_info(songs[0]['albumId'], include_tracks=False) response = requests.get(album_info['albumArtRef']) t = magic.from_buffer(response.content, mime=True) if t == 'image/jpeg': ext = '.jpg' elif t == 'image/png': ext = '.png' else: print('Unknown MIME type: {}'.format(t), file=sys.stderr) ext = '.wat' with open(os.path.join(dname, dname + ext), 'wb') as fout: fout.write(response.content)
def _GetLibrary(): print("Retrieving library..") api = Mobileclient() if not api.login(config["DEFAULT"]["sGMusicUsername"], config["DEFAULT"]["sGMusicPassword"], Mobileclient.FROM_MAC_ADDRESS): raise Exception("Google Music login attempt failed under username:"******"DEFAULT"]["sGMusicUsername"]) library = api.get_all_songs() GMRLog.debug("library:" + TM.Narrate(library)) return library
def main(path): api = Mobileclient() logged_in = api.login(EMAIL, PASSWORD) if logged_in: print("Succesfully logged in, retrieving all songs and running check...") all_songs = api.get_all_songs() tracks = remove_dups(all_songs) results = find_in_lib(path, tracks) print("saving file to %s" % os.path.join(os.getcwd(), "output.json")) with open("output.json", "w+") as f: f.write(json.dumps(results)) f.close()
def get_online_library(): api = Mobileclient() logged_in = api.login("*****@*****.**", "yesilly12twat", Mobileclient.FROM_MAC_ADDRESS) if logged_in == False: sys.exit("Couldn't log in.") if not api.is_authenticated: sys.exit("Couldn't log in. Wrong credentials") library = { "tracks": api.get_all_songs(), "playlists": api.get_all_user_playlist_contents(), } return library
def get_lib_from_googlemusic(): # global USER_NAME # global APP_PASSWORD api = Mobileclient(debug_logging=False) logged_in = api.login(email=USER_NAME, password=APP_PASSWORD, locale='en_US', android_id=Mobileclient.FROM_MAC_ADDRESS) lib = api.get_all_songs() lib_list = [] for song in lib: song_dict={} song_dict['artist'] = song['artist'] # song_dict['album'] = song['album'] song_dict['title'] = song['title'] lib_list.append(song_dict) return pd.DataFrame(lib_list)
class Plugin: name = 'gmusic' def __init__(self, username, password): self.client = Mobileclient() self.client.login(username, password, Mobileclient.FROM_MAC_ADDRESS) # self.webclient = Webclient() # self.webclient.login(username, password) def get_tracks(self, artist=None, album=None): """ Fetches tracks from api. If no filter is defined, it will get user tracks """ return TrackList(self.client.get_all_songs()) def get_playlists(self): """ Get playlists and radios """ playlists = [] for playlist in self.client.get_all_user_playlist_contents(): tracks = TrackList([self.client.get_track_info(x['trackId']) for x in playlist['tracks']]) playlists.append(PlayList(playlist['name'], tracks)) return playlists def stream(self, track): def _stream(url): inp = requests.get(url, stream=True) chunk_size = 1024 for chunk in inp.iter_content(chunk_size): if not chunk: continue yield chunk song_id = track.uri.split(':')[-1] return _stream(self.client.get_stream_url(song_id)) def search(self, keywords, matches): results = self.client.search(keywords) if matches == 'artist': return {'artists': results.get('artist_hits', [])} elif matches == 'album': return {'albums': results.get('album_hits', [])} elif matches == 'tracks': return {'tracks': results.get('song_hits', [])} elif matches == 'all': return {'artists': results.get('artist_hits', []), 'albums': results.get('album_hits', []), 'tracks': results.get('song_hits', [])}
def play_song(): """ Need OAuth creds first: from gmusicapi import Mobileclient api = Mobileclient() api.perform_oauth() # do instructions, it will save creds to storage. """ api = Mobileclient() api.oauth_login(Mobileclient.FROM_MAC_ADDRESS) library = api.get_all_songs() track_id = library[0]['id'] url = api.get_stream_url(track_id) p = vlc.MediaPlayer(url) p.play()
def getTracksGPM(api): #Get unformatted tracks list tracksDictionary = Mobileclient.get_all_songs(api, False, None) tracksList = [] for i in range(len(tracksDictionary)): trackName = tracksDictionary[i]['title'].upper() artistName = tracksDictionary[i]['artist'].upper() tracksList.append(trackFormat(trackName, artistName)) return tracksList
def lastfm_to_google_music(lastfm_user, lastfm_apikey, gmusic_email, gmusic_password): lastfm_client = LastFMClient() gmusic_client = Mobileclient() gmusic_client.login(gmusic_email, gmusic_password) tracks= defaultdict(list) # get gmusic all songs gmusic_load = gmusic_client.get_all_songs() for element in gmusic_load : rp_artist = element["artist"].encode('utf8').lower() rp_title = element["title"].encode('utf8').lower() tracks["gmusic"].append((rp_artist,rp_title)) # get lastfm loves lastfm_likes = lastfm_client.liked_tracks(lastfm_user, lastfm_apikey) json_string = json.loads(lastfm_likes) for element in json_string["lovedtracks"]["track"]: rp_artist = element["artist"]["name"].encode('utf8').lower() rp_title = element["name"].encode('utf8').lower() tracks["lastfm"].append((rp_artist,rp_title)) match = set(tracks["lastfm"]) & set(tracks["gmusic"]) print "gmusic total" print sum(len(x) for x in tracks["gmusic"]) print "Lastfm likes total" lastfm_like_count = sum(len(x) for x in tracks["lastfm"]) print lastfm_like_count print "matched with gmusic" print sum(len(x) for x in match) print "not matched" print list(set(tracks["lastfm"]) - set(match)) for element in gmusic_load: rp_artist = element["artist"].encode('utf8').lower() rp_title = element["title"].encode('utf8').lower() for element2 in match: if element2 == (rp_artist,rp_title): element['rating'] = '5' gmusic_client.change_song_metadata(element)
def main(): api = Mobileclient() print('login as {} to Google Music...'.format(CONFIG['login'])) if api.login(CONFIG['login'], CONFIG['password'], CONFIG['device_id']): try: if not api.is_subscribed: raise AssertionError( 'You has not Google Music subscription :(') print('Reading all songs...') lib = api.get_all_songs() albums = CONFIG['albums'] for artist in albums: print('Artist: {}'.format(artist)) for album in albums[artist]: print('Album: {}'.format(album)) tracks = sorted([ t for t in lib if t['album'] == album and t['artist'] == artist ], key=lambda t: t['trackNumber']) if tracks: dir_name = os.path.join( CONFIG['root_dir'], fix_file_name(artist), "{} {}".format(tracks[0]['year'], fix_file_name(album))) if not os.path.exists(dir_name): os.makedirs(dir_name) for tr in tracks: if 'albumArtRef' in tr: i = 0 for artRef in tr['albumArtRef']: img_file_name = 'cover.{}.jpg' if i else 'cover.jpg' full_path = os.path.join( dir_name, img_file_name) full_path = fix_path_name(full_path) if not os.path.exists(full_path): wget.download(artRef['url'], full_path) file_name = '{:02d} {}.mp3'.format( tr['trackNumber'], tr['title']) file_name = fix_file_name(file_name) full_path = os.path.join(dir_name, file_name) if not os.path.exists(full_path): url = api.get_stream_url(tr['id']) wget.download(url, full_path) update_tags(tr, full_path) finally: api.logout()
class GoogleMusicClient(object): def __init__(self, email, password, device_id): self.api = Mobileclient() self.api.login(email, password) self.device_id = device_id self.playlists = [] self.songs = {} self.player = Player() def update_library(self): self.songs = {song.get('nid'): song for song in self.api.get_all_songs()} self.playlists = [ Playlist(name=playlist['name'].encode('utf-8'), tracks=[track['trackId'] for track in playlist['tracks']]) for playlist in self.api.get_all_user_playlist_contents() ] def play(self, item_type, item_index): if item_type == 'playlist': click.echo('playing {} {}'.format(item_type, self.playlists[int(item_index)].name)) def queue(self, item_type, item_index): if item_type == 'playlist': for song in self.playlists[int(item_index)]: self.player.enqueue(song) def show(self, item_type, item_index): if item_type == 'playlist': playlist = self.playlists[int(item_index)] click.echo('showing {} {}'.format(item_type, playlist.name)) for song in playlist.tracks: click.echo(self.songs[song]) elif item_type == 'queue': for song in self.player.queue: click.echo(song) def list(self, item_type): if item_type == 'playlist': for i, playlist in enumerate(self.playlists): click.echo("[{}]\t{} ({})".format(i, playlist.name, len(playlist.tracks))) elif item_type == 'songs': click.echo(self.songs)
def main(): global waiting waiting = False mc = Mobileclient() mc.perform_oauth(storage_filepath='./oauth.cred') mc.oauth_login(device_id=mc.FROM_MAC_ADDRESS, oauth_credentials='./oauth.cred') waiting = True thread = Thread(target=threaded_cursor, args=('Gathering Metadata', )) thread.start() library = mc.get_all_songs() years = {} for song in library: if 'year' in song: try: if not str(song['year']) in years: years[str(song['year'])] = [] if any(song['album'] in keys['album'] for keys in years[str(song['year'])]): continue else: if 'albumArtRef' in song: d = { 'album': song['album'], 'artwork': song['albumArtRef'][0]['url'] } years[str(song['year'])].append(d) else: if verbose: print("No album art for {}".format(song)) except KeyError: if verbose: print("Key error {}".format(song)) else: if verbose: print("No year for {}".format(song)) # clean up songs with unknown year if '0' in years: del years['0'] waiting = False thread.join() make_plot(years)
def analyse(self, device_id, artists_number): """The main method that analysis Google Play Music account, and returns results of parsing concert.ua :param device_id: str :param artists_number: int :return: results: list """ api = Mobileclient() try: artists_number = int(artists_number) device_id = str(device_id) except ValueError: return -1 api.oauth_login(str(device_id)) main_playlist = Playlist() library = api.get_all_songs() for song in library: main_playlist.append(song) playlists = api.get_all_user_playlist_contents() for playlist in playlists: for song in playlist['tracks']: main_playlist.append(song) top = main_playlist.top_n(artists_number) artists = [] for artist_id in top: artist = api.get_artist_info(include_albums=False, artist_id=artist_id, max_rel_artist=0, max_top_tracks=0) artists.append(artist['name']) results = [] for artist in artists: parse_result = Parser().parse(artist) if parse_result: results.append(parse_result) return results
def download_all(outdir: str = DEF_OUT_DR, device_id: str = DEF_DEV_ID, mac_addr: str = DEF_MAC_AD): mc = Mobileclient() mm = Musicmanager() mc.oauth_login(DEF_DEV_ID) mm.login(uploader_id=DEF_MAC_AD) songs = mc.get_all_songs() print(f'Downloading {len(songs)}') for song in songs: filename, audio = mm.download_song(song['id']) print(f'Downloading {song["id"]} : {filename}', flush=True) with open(f'{outdir}/{filename}', 'wb') as fh: fh.write(audio) with open(f'{outdir}/{filename}.meta', 'w') as fh: json.dump(song, fh, indent=2, sort_keys=True)
def get_library(mobile_client: Mobileclient) -> List[GpmTrack]: """ Given an authenticated mobile client, return a list of GPM Tracks representing the users library Args: mobile_client (Mobileclient): Must be authenticated. Client is used to retrieve the library for the GPM user. Returns: List[GpmTrack]: A list of ``GpmTrack`` objects representing the user's library. """ # Check if client isn't authenticated if not mobile_client.is_authenticated(): raise UnauthenticatedClientException("Trying to get library with an unauthenticated mobile client") # Get the library as a list of dicts raw_tracks: List[dict] = mobile_client.get_all_songs() return [ApiWrapper.__map_dict_to_gpm_track(track) for track in raw_tracks]
class GMusicClient: def __init__(self): """ This connects to the google music server by requesting credentials. """ self.api = Mobileclient() username = input('Type your Google Play Music email below.\n--> ') dir_path = os.path.dirname( os.path.realpath(__file__)) + '/.cache-gmusic-' + ''.join( filter(str.isalpha, username)) # Check if already authenticated if (not os.path.isfile(dir_path)): self.api.perform_oauth(open_browser=True, storage_filepath=dir_path) # Attempt to log in; if fail, get new token. try: self.api.oauth_login(Mobileclient.FROM_MAC_ADDRESS) except: self.api.perform_oauth(open_browser=True, storage_filepath=dir_path) self.api.oauth_login(Mobileclient.FROM_MAC_ADDRESS) print('Connected to GMusic') def get_playlists(self): """ Gets all the playlists in Google Play Music. Some may not actually have any music, but they will be processed anyways. """ return Playlists(self.api.get_all_user_playlist_contents()) def get_all(self): """ Gets the entire Google library for adding to the """ print('Requesting Google library') library = self.api.get_all_songs() print('Received Google library, we have', len(library), 'songs') return MusicLibrary(library)
def bestof(username, password): pass api = Mobileclient() api.login(username, password, Mobileclient.FROM_MAC_ADDRESS) library = api.get_all_songs() for year in range(2018, 2010, -1): songs = [s for s in library if 'year' in s and s['year'] == year] albums = set([(s['artist'], s['album']) for s in songs]) plays = [] for artist, album in albums: plays.append((sum([s['playCount'] if 'playCount' in s else 0 for s in songs if s['album'] == album]), artist, album)) plays = sorted(plays, reverse=True) print('===== {} ====='.format(year)) for play_count, artist, album in plays: print('{} - {} ({})'.format(artist, album, play_count)) print()
class Kotone: def __init__(self, device_id: str, cred_mc: oauth2client.client.OAuth2Credentials, cred_mm: oauth2client.client.OAuth2Credentials): self._mc = Mobileclient() if not self._mc.oauth_login(device_id, cred_mc): raise RuntimeError('Mobileclient login failed') self._mm = Musicmanager() if not self._mm.login(cred_mm, _UPLOADER_ID, _UPLOADER_NAME): raise RuntimeError('Musicmanager login failed') def get_songs(self): return self._mc.get_all_songs() def download_song(self, song_id: str) -> Tuple[str, bytes]: return self._mm.download_song(song_id) def stream_url(self, song_id: str) -> str: return self._mc.get_stream_url(song_id) def upload(self, paths): return self._mm.upload(paths)
def home(request): sync = request.params.get('sync', None) api = Mobileclient() api.login(GOOGLE_EMAIL, GOOGLE_MUSIC_PASS, Mobileclient.FROM_MAC_ADDRESS) if os.path.isfile('songs.json') and not sync: f = open('songs.json', 'r') songs = f.read() songs = json.loads(songs) else: songs = api.get_all_songs() songs = list(reversed(resort_by_added(songs))) f = open('songs.json', 'w') f.write(json.dumps(songs, indent=4, separators=(',', ': '))) f.close() result = [] back = timedelta(days=10) now = datetime.now() for track in songs: d = get_datetime(track['creationTimestamp']) if (now - d) < back: result.append({ 'name': track['title'], 'url': api.get_stream_url(track['id'], GOOGLE_DEVICE_ID), 'date': d.strftime("%a %d-%m-%Y %H:%M") }) master = get_renderer('templates/master.pt').implementation() return {'song_list': result, 'title': "Song list", 'main': master}
def main(): api = Mobileclient() print('login as {} to Google Music...'.format(CONFIG['login'])) if api.login(CONFIG['login'], CONFIG['password'], CONFIG['device_id']): if not api.is_subscribed: raise AssertionError('You has not Google Music subscription :(') lib = api.get_all_songs() print('Reading all songs...') artist_album = {} for tr in lib: artist = tr['artist'] album = tr['album'] if artist not in artist_album: artist_album[artist] = [ album, ] if album not in artist_album[artist]: artist_album[artist] += [ album, ] print(json.dumps(artist_album, indent=2, ensure_ascii=False)) api.logout()
from gmusicapi import Mobileclient import codecs api = Mobileclient() trackEntryStart = "sj#track" trackPrefix = "'title'" artistPrefix = "'artist'" delim = "," if not api.oauth_login(api.FROM_MAC_ADDRESS): api.perform_oauth() response = str(api.get_all_songs()) index = 0 f = codecs.open("GPMLibraryParsed.txt", mode="w", encoding="utf-8") result = "" while index < len(response): if response.find(trackEntryStart, index) != -1: index = response.index(trackEntryStart, index) titleIndex = response.index(trackPrefix, index) + 10 titleDelimIndex = response.index(delim, titleIndex) - 1 artistIndex = response.index(artistPrefix, index) + 11 artistDelimIndex = response.index(delim, artistIndex) - 1 result = result + response[
api = Mobileclient() api.login(args.account, password, Mobileclient.FROM_MAC_ADDRESS) print("Login successful") print("Fetching all playlists") googlePlayPlaylists = api.get_all_user_playlist_contents() print("Fetching list of tracks for the playlist") googlePlayPlaylistTrackList = [playlist['tracks'] for playlist in googlePlayPlaylists if playlist['name'] == args.playlist][0] googlePlayTrackIds = list(map(lambda x: x['trackId'], googlePlayPlaylistTrackList)) print("Fetching list of files in Google Play library") allTracksInGooglePlayLibrary = api.get_all_songs() print("Fetching track info for tracks in playlist") tracksInGooglePlayPlaylist = [track for track in allTracksInGooglePlayLibrary if track['id'] in googlePlayTrackIds] googlePlayTrackInfos = \ list(map(lambda x: {'artist': x['artist'], 'title': x['title'], 'comment': x['comment'], 'album': x['album']}, tracksInGooglePlayPlaylist)) def googlePlayPlaylistContainsTrack(tags): for info in googlePlayTrackInfos: if 'ARTIST' in tags and 'TITLE' in tags and info['artist'] == tags['ARTIST'][0] and info['title'] == tags['TITLE'][0]: # TODO: remove track from infos googlePlayTrackInfos.remove(info) print("Found a match for: {} - {}".format(tags['ARTIST'][0], tags['TITLE'][0]))
class GMusicSession(object): def __init__(self): super(GMusicSession, self).__init__() logger.info('Mopidy uses Google Music') self.api = Mobileclient() def login(self, username, password, deviceid): if self.api.is_authenticated(): self.api.logout() try: self.api.login(username, password) except CallFailure as error: logger.error(u'Failed to login as "%s": %s', username, error) if self.api.is_authenticated(): if deviceid is None: self.deviceid = self.get_deviceid(username, password) else: self.deviceid = deviceid else: return False def logout(self): if self.api.is_authenticated(): return self.api.logout() else: return True def get_all_songs(self): if self.api.is_authenticated(): return self.api.get_all_songs() else: return {} def get_stream_url(self, song_id): if self.api.is_authenticated(): try: return self.api.get_stream_url(song_id, self.deviceid) except CallFailure as error: logger.error(u'Failed to lookup "%s": %s', song_id, error) def get_all_playlist_contents(self): if self.api.is_authenticated(): return self.api.get_all_user_playlist_contents() else: return {} def get_shared_playlist_contents(self, shareToken): if self.api.is_authenticated(): return self.api.get_shared_playlist_contents(shareToken) else: return {} def get_all_playlists(self): if self.api.is_authenticated(): return self.api.get_all_playlists() else: return {} def get_deviceid(self, username, password): logger.warning(u'No mobile device ID configured. ' u'Trying to detect one.') webapi = Webclient(validate=False) webapi.login(username, password) devices = webapi.get_registered_devices() deviceid = None for device in devices: if device['type'] == 'PHONE' and device['id'][0:2] == u'0x': # Omit the '0x' prefix deviceid = device['id'][2:] break webapi.logout() if deviceid is None: logger.error(u'No valid mobile device ID found. ' u'Playing songs will not work.') else: logger.info(u'Using mobile device ID %s', deviceid) return deviceid def get_track_info(self, store_track_id): if self.api.is_authenticated(): try: return self.api.get_track_info(store_track_id) except CallFailure as error: logger.error(u'Failed to get All Access track info: %s', error) def get_album_info(self, albumid, include_tracks=True): if self.api.is_authenticated(): try: return self.api.get_album_info(albumid, include_tracks) except CallFailure as error: logger.error(u'Failed to get All Access album info: %s', error)
def main(): log.setLevel(logging.INFO) logging.getLogger('gmusicapi').setLevel(logging.INFO) cred_path = os.path.join(os.path.expanduser('~'), '.gmusic-sync') if not os.path.isfile(cred_path): raise NoCredentialException( 'No username/password was specified. No config file could ' 'be found either. Try creating %s and specifying your ' 'username/password there. Make sure to chmod 600.' % cred_path) if not oct(os.stat(cred_path)[os.path.stat.ST_MODE]).endswith('00'): raise NoCredentialException( 'Config file is not protected. Please run: ' 'chmod 600 %s' % cred_path) config = ConfigParser.ConfigParser() config.read(cred_path) src_user = config.get('src','username') src_pass = config.get('src','password') src_device = config.get('src','deviceid') dst_user = config.get('dst','username') dst_pass = config.get('dst','password') dst_device = config.get('dst','deviceid') if not src_user or not src_pass or not dst_user or not dst_pass: raise NoCredentialException( 'No username/password could be read from config file' ': %s' % cred_path) if not src_device or not dst_device: raise NoCredentialException( 'No deviceId could be read from config file' ': %s' % cred_path) parser = argparse.ArgumentParser(description='gmusic-sync', add_help=False) parser.add_argument('-a', '--add-songs', help='Add songs from the src account to the dst account', action='store_true', dest='add') parser.add_argument('-h', '--heuristics', help='Use heuristics to try and find missing songs. Songs must match artist and title to be considered a match.', action='store_true', dest='heuristics') parser.add_argument('-hs', '--strict-heuristics', help='Songs must match artist, album, and title to be considered a match.', action='store_true', dest='strict_heuristics') args = parser.parse_args() api = Mobileclient() api.login(src_user, src_pass, src_device) library = api.get_all_songs() api2 = Mobileclient() api2.login(dst_user, dst_pass, dst_device) library2 = api2.get_all_songs() aa_songs2 = [] aa_diff = [] for track in library2: try: if track['storeId'].startswith('T'): aa_songs2.append(track['storeId']) except KeyError: continue for track in library: try: if track['storeId'].startswith('T') : if track['storeId'] not in aa_songs2: #aa_diff.append(track) if args.add: api2.add_aa_track(track['storeId']) time.sleep(1) # sleep so Google doesn't banhammer you for spamming their servers except KeyError: continue except: #Track wasn't found in the dst database. See if we can match to an existing song or something #else from the store if args.heuristics: if heuristic_search(library2, track, args.strict_heuristics) is not None: continue print 'ERROR ADDING: ' + track['title'] + ' - ' + track['artist'] + ' (' + track['album'] + ')' + ' (' + track['storeId'] + ')' aa_diff.append(track) continue print '----------------- TRACKS NOT IN DST --------------------' for track in aa_diff: print track['title'] + ' - ' + track['artist'] + ' (' + track['album'] + ')'
import sys import getpass from gmusicapi import Mobileclient email = raw_input("User (gmail): ") pswd = getpass.getpass("Password:"******"Authentification error" sys.exit() songs = api.get_all_songs() for song in songs: song["genre"] = song["genre"].replace(" ", "").lower() if ";" in song["genre"]: song["genre"] = song["genre"].split(";") elif "," in song["genre"]: song["genre"] = song["genre"].split(",") elif "/" in song["genre"]: song["genre"] = song["genre"].split("/") else: song["genre"] = [song["genre"]] genres_map = {} for s in songs: for g in s["genre"]: genres_map.setdefault(g, [])
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"]])
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: title = to_ascii(self.now_playing_song.get('title')) artist = to_ascii(self.now_playing_song.get('artist')) logging.info("Now playing %s by %s", 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: album = to_ascii(self.now_playing_song.get('album')) duration = to_ascii \ (self.now_playing_song.get('durationMillis')) logging.info("album %s duration %s", 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: try: track = self.now_playing_song['trackNumber'] total = self.now_playing_song['totalTrackCount'] logging.info("track number %s total tracks %s", 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: try: year = song['year'] logging.info("track year %s", 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 arg not 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.encode('utf-8'), \ name.encode('utf-8'))) break if not artist: # Play some random artist from the library random.seed() artist = random.choice(self.library.keys()) artist = self.library[artist] print_wrn("[Google Play Music] '{0}' not found. "\ "Feeling lucky?." \ .format(arg.encode('utf-8'))) else: artist = self.library[arg] tracks_added = 0 for album in artist: tracks_added += self.__enqueue_tracks(artist[album]) print_wrn("[Google Play Music] Playing '{0}'." \ .format(to_ascii(artist))) 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 and tentative_album: album = tentative_album artist = tentative_artist print_wrn("[Google Play Music] '{0}' not found. " \ "Playing '{1}' instead." \ .format(arg.encode('utf-8'), \ album.encode('utf-8'))) if not album: # Play some random album from the library random.seed() artist = random.choice(self.library.keys()) album = random.choice(self.library[artist].keys()) print_wrn("[Google Play Music] '{0}' not found. "\ "Feeling lucky?." \ .format(arg.encode('utf-8'))) if not album: raise KeyError("Album not found : {0}".format(arg)) self.__enqueue_tracks(self.library[artist][album]) print_wrn("[Google Play Music] Playing '{0}'." \ .format(to_ascii(album))) 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 playlist_name = None for name, plist in self.playlists.items(): print_nfo("[Google Play Music] [Playlist] '{0}'." \ .format(to_ascii(name))) if arg not in self.playlists.keys(): for name, plist in self.playlists.iteritems(): if arg.lower() in name.lower(): playlist = plist playlist_name = name print_wrn("[Google Play Music] '{0}' not found. " \ "Playing '{1}' instead." \ .format(arg.encode('utf-8'), \ to_ascii(name))) break if not playlist: # Play some random playlist from the library random.seed() playlist_name = random.choice(self.playlists.keys()) playlist = self.playlists[playlist_name] print_wrn("[Google Play Music] '{0}' not found. "\ "Feeling lucky?." \ .format(arg.encode('utf-8'))) else: playlist_name = arg playlist = self.playlists[arg] self.__enqueue_tracks(playlist) print_wrn("[Google Play Music] Playing '{0}'." \ .format(to_ascii(playlist_name))) 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 add 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 add 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 arg not in all_genres: genre = next((g for g in all_genres \ if arg.lower() in to_ascii(g['name']).lower()), \ None) tracks_added = 0 while not tracks_added: if not genre and len(all_genres): # Play some random genre from the search results random.seed() genre = random.choice(all_genres) print_wrn("[Google Play Music] '{0}' not found. "\ "Feeling lucky?." \ .format(arg.encode('utf-8'))) 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 %d tracks from %s to queue", tracks_added, genre_name) if not tracks_added: # This will produce another iteration in the loop genre = None print_wrn("[Google Play Music] Playing '{0}'." \ .format(to_ascii(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_situation_unlimited(self, arg): """Search Unlimited for a situation with a given name and add its tracks to the playback queue. Requires Unlimited subscription. """ print_msg("[Google Play Music] [Retrieving situations] : '{0}'. " \ .format(self.__email)) try: self.__enqueue_situation_unlimited(arg) if not len(self.queue): raise KeyError logging.info("Added %d tracks from %s to queue", \ len(self.queue), arg) except KeyError: raise KeyError("Situation 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 = self.__gmusic_search(arg, 'artist') 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 %d tracks from %s to queue", \ 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 add its tracks to the playback queue. Requires Unlimited subscription. """ try: album = self.__gmusic_search(arg, 'album') album_tracks = dict() if album: album_tracks = self.__gmusic.get_album_info \ (album['album']['albumId'])['tracks'] if not album_tracks: raise KeyError print_wrn("[Google Play Music] Playing '{0}'." \ .format((album['album']['name']).encode('utf-8'))) tracks_added = self.__enqueue_tracks(album_tracks) logging.info("Added %d tracks from %s to queue", \ 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: max_results = 200 track_hits = self.__gmusic.search(arg, max_results)['song_hits'] if not len(track_hits): # Do another search with an empty string track_hits = self.__gmusic.search("", max_results)['song_hits'] print_wrn("[Google Play Music] '{0}' not found. "\ "Feeling lucky?." \ .format(arg.encode('utf-8'))) tracks = list() for hit in track_hits: tracks.append(hit['track']) tracks_added = self.__enqueue_tracks(tracks) logging.info("Added %d tracks from %s to queue", \ 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 u'id' not 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 %d Unlimited promoted tracks to queue", \ 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 """ 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 song_artist not in self.library: self.library[song_artist] = CaseInsensitiveDict() self.library[song_artist][self.all_songs_album_title] = list() if song_album not 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 : %s", to_ascii(artist)) for album in self.library[artist].keys(): logging.info(" Album : %s", 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 : %s", 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 arg not 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 = list() 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 %d tracks from %s to queue", 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}' " \ "not found in the user's library. " \ .format(arg.encode('utf-8'))) def __enqueue_station_unlimited(self, arg, max_results=200, quiet=False): """Search for a station and enqueue all of its tracks (Unlimited) """ if not quiet: print_msg("[Google Play Music] [Station search in "\ "Google Play Music] : '{0}'. " \ .format(arg.encode('utf-8'))) try: station_name = arg station_id = None station = self.__gmusic_search(arg, 'station', max_results, quiet) 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 playlist_token = seed['playlistShareToken'] if seed_type == u'8' else None curated_station_id = seed['curatedStationId'] if seed_type == u'9' else None num_tracks = max_results tracks = list() try: station_id \ = self.__gmusic.create_station(station_name, \ track_id, \ artist_id, \ album_id, \ genre_id, \ playlist_token, \ curated_station_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: if not quiet: print_wrn("[Google Play Music] [Station] : '{0}'." \ .format(station_name.encode('utf-8'))) logging.info("Added %d tracks from %s to queue", \ tracks_added, arg.encode('utf-8')) self.__update_play_queue_order() except KeyError: raise KeyError("Station not found : {0}".format(arg)) def __enqueue_situation_unlimited(self, arg): """Search for a situation and enqueue all of its tracks (Unlimited) """ print_msg("[Google Play Music] [Situation search in "\ "Google Play Music] : '{0}'. " \ .format(arg.encode('utf-8'))) try: situation_hits = self.__gmusic.search(arg)['situation_hits'] if not len(situation_hits): # Do another search with an empty string situation_hits = self.__gmusic.search("")['situation_hits'] print_wrn("[Google Play Music] '{0}' not found. "\ "Feeling lucky?." \ .format(arg.encode('utf-8'))) situation = next((hit for hit in situation_hits \ if 'best_result' in hit.keys()), None) num_tracks = 200 if not situation and len(situation_hits): max_results = num_tracks / len(situation_hits) for hit in situation_hits: situation = hit['situation'] print_nfo("[Google Play Music] [Situation] '{0} : {1}'." \ .format((hit['situation']['title']).encode('utf-8'), (hit['situation']['description']).encode('utf-8'))) self.__enqueue_station_unlimited(situation['title'], max_results, True) if not situation: raise KeyError except KeyError: raise KeyError("Situation not found : {0}".format(arg)) def __enqueue_tracks(self, tracks): """ Add tracks to the playback queue """ count = 0 for track in tracks: if u'id' not 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 : %s", 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 : %s", 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 def __gmusic_search(self, query, query_type, max_results=200, quiet=False): """ Search Google Play (Unlimited) """ search_results = self.__gmusic.search(query, max_results)[query_type + '_hits'] result = next((hit for hit in search_results \ if 'best_result' in hit.keys()), None) if not result and len(search_results): secondary_hit = None for hit in search_results: if not quiet: print_nfo("[Google Play Music] [{0}] '{1}'." \ .format(query_type.capitalize(), (hit[query_type]['name']).encode('utf-8'))) if query.lower() == \ to_ascii(hit[query_type]['name']).lower(): result = hit break if query.lower() in \ to_ascii(hit[query_type]['name']).lower(): secondary_hit = hit if not result and secondary_hit: result = secondary_hit if not result and not len(search_results): # Do another search with an empty string search_results = self.__gmusic.search("")[query_type + '_hits'] if not result and len(search_results): # Play some random result from the search results random.seed() result = random.choice(search_results) if not quiet: print_wrn("[Google Play Music] '{0}' not found. "\ "Feeling lucky?." \ .format(query.encode('utf-8'))) return result
from gmusicapi import Mobileclient client = Mobileclient() assert client.login('*****@*****.**', PASSWORD, Mobileclient.FROM_MAC_ADDRESS) tracks = client.get_all_songs() genres_to_merge = {'Alternative Rock', 'Alt. Rock'} playlist_track_ids = [t['id'] for t in tracks if t['genre'] in genres_to_merge] print "Creating playlist with %d tracks" % len(playlist_track_ids) playlist_id = client.create_playlist('Alt Rock') client.add_songs_to_playlist(playlist_id, playlist_track_ids)
class MusicLibrary(object): 'Read information about your Google Music library' def __init__(self, username=None, password=None, true_file_size=False, scan=True, verbose=0): self.verbose = False if verbose > 1: self.verbose = True self.__login_and_setup(username, password) self.__artists = {} # 'artist name' -> {'album name' : Album(), ...} self.__albums = [] # [Album(), ...] if scan: self.rescan() self.true_file_size = true_file_size def rescan(self): self.__artists = {} # 'artist name' -> {'album name' : Album(), ...} self.__albums = [] # [Album(), ...] self.__aggregate_albums() def __login_and_setup(self, username=None, password=None): # If credentials are not specified, get them from $HOME/.gmusicfs if not username or not password: cred_path = os.path.join(os.path.expanduser('~'), '.gmusicfs') if not os.path.isfile(cred_path): raise NoCredentialException( 'No username/password was specified. No config file could ' 'be found either. Try creating %s and specifying your ' 'username/password there. Make sure to chmod 600.' % cred_path) if not oct(os.stat(cred_path)[os.path.stat.ST_MODE]).endswith('00'): raise NoCredentialException( 'Config file is not protected. Please run: ' 'chmod 600 %s' % cred_path) self.config = ConfigParser.ConfigParser() self.config.read(cred_path) username = self.config.get('credentials','username') password = self.config.get('credentials','password') global deviceId deviceId = self.config.get('credentials','deviceId') if not username or not password: raise NoCredentialException( 'No username/password could be read from config file' ': %s' % cred_path) if not deviceId: raise NoCredentialException( 'No deviceId could be read from config file' ': %s' % cred_path) self.api = GoogleMusicAPI(debug_logging=self.verbose) log.info('Logging in...') self.api.login(username, password) log.info('Login successful.') def __aggregate_albums(self): 'Get all the tracks in the library, parse into artist and album dicts' all_artist_albums = {} # 'Artist|||Album' -> Album() log.info('Gathering track information...') tracks = self.api.get_all_songs() for track in tracks: # Prefer the album artist over the track artist if there is one: artist = formatNames(track['albumArtist'].lower()) if artist.strip() == '': artist = formatNames(track['artist'].lower()) # Get the Album object if it already exists: key = '%s|||%s' % (formatNames(artist), formatNames(track['album'].lower())) album = all_artist_albums.get(key, None) if not album: # New Album if artist == '': artist = 'unknown' album = all_artist_albums[key] = Album( self, formatNames(track['album'].lower())) self.__albums.append(album) artist_albums = self.__artists.get(artist, None) if artist_albums: artist_albums[formatNames(album.normtitle)] = album else: self.__artists[artist] = {album.normtitle: album} artist_albums = self.__artists[artist] album.add_track(track) log.debug('%d tracks loaded.' % len(tracks)) log.debug('%d artists loaded.' % len(self.__artists)) log.debug('%d albums loaded.' % len(self.__albums)) def get_artists(self): return self.__artists def get_albums(self): return self.__albums def get_artist_albums(self, artist): log.debug(artist) return self.__artists[artist] def cleanup(self): pass
def handle(self, *args, **options): if GPLAY_PASS == "" or GPLAY_USER == "": self.stdout.write('Credentials not set up. Please edit settings.py') return api = Mobileclient() if not api.login(GPLAY_USER,GPLAY_PASS): self.stdout.write('Incorrect credentials, login failed') return self.stdout.write('Connected to Google Music, downloading data...') #library = [] library = api.get_all_songs() self.stdout.write('Data downloaded!') self.stdout.write('Clearing DB...') cursor = connection.cursor() # This can take a long time using ORM commands on the Pi, so lets Truncate cursor.execute('DELETE FROM ' + Track._meta.db_table) cursor.execute('DELETE FROM ' + Album._meta.db_table) cursor.execute('DELETE FROM ' + Artist._meta.db_table) cursor.execute('DELETE FROM ' + Playlist._meta.db_table) cursor.execute('DELETE FROM ' + PlaylistConnection._meta.db_table) self.stdout.write('Parsing new data...') # Easier to keep track of who we've seen like this... artists = [] albums = [] count = len(library) self.stdout.write(str(count) + ' tracks found') i = 0 for song in library: i = i + 1 track = Track() if song['albumArtist'] == "": if song['artist'] == "": a = "Unknown Artist" else: a = song['artist'] else: a = song['albumArtist'] if a not in artists: artist = Artist() artist.name = a try: artist.art_url = song['artistArtRef'][0]['url'] except: print "No Art found." artist.save() artists.append(a) self.stdout.write('Added artist: ' + a) self.stdout.write(str(i) + '/' + str(count) + ' tracks completed') else: artist = Artist.objects.get(name=a) track.artist = artist if song['album'] + a not in albums: album = Album() album.name = song['album'] album.artist = artist try: album.year = song['year'] except: pass try: album.art_url = song['albumArtRef'][0]['url'] except: print "No Art found." album.save() albums.append(song['album'] + a) else: album = Album.objects.get(name=song['album'], artist=artist) track.album = album track.name = song['title'] track.stream_id = song['id'] try: track.track_no = song['trackNumber'] except: track.track_no = 0 track.save() self.stdout.write('All tracks saved!') self.stdout.write('Getting Playlists...') self.stdout.write('Saving playlists...') self.stdout.write('Getting playlist contents.') playlists = api.get_all_user_playlist_contents() for playlist in playlists: p = Playlist() p.pid = playlist['id'] p.name = playlist['name'] p.save() for entry in playlist['tracks']: try: track = Track.objects.get(stream_id=entry['trackId']) pc = PlaylistConnection() pc.playlist = p pc.track = track pc.save() except Exception: print "Not found." self.stdout.write('Library saved!')
def fetch(self): client = Mobileclient() assert client.login(USER, PASSWORD, Mobileclient.FROM_MAC_ADDRESS) self.data = client.get_all_songs()
class GPMClient(object): """ Google Play Music client. """ all_songs_album_title = "All Songs" thumbs_up_playlist_name = "Thumbs Up" #------------------------------------------------------------------------------ def __init__(self, email, password, device_id): self.__api = Mobileclient() self.logged_in = False self.__device_id = device_id attempts = 0 while not self.logged_in and attempts < 3: self.logged_in = self.__api.login(email, password, device_id) attempts += 1 self.all_tracks = dict() self.playlists = dict() self.library = dict() #------------------------------------------------------------------------------ def logout(self): self.__api.logout() #------------------------------------------------------------------------------ def update_local_lib(self): songs = self.__api.get_all_songs() self.playlists[self.thumbs_up_playlist_name] = list() # Get main library song_map = dict() 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"] song_map[song_id] = song if song_artist == "": song_artist = "Unknown Artist" if song_album == "": song_album = "Unknown Album" if song_artist not in self.library: self.library[song_artist] = dict() self.library[song_artist][self.all_songs_album_title] = list() if song_album not 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(): for album in self.library[artist].keys(): 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 # Get all playlists plists = self.__api.get_all_user_playlist_contents() for plist in plists: plist_name = plist["name"] self.playlists[plist_name] = list() for track in plist["tracks"]: if track["trackId"] not in song_map: song = song_map[track["trackId"]] = track["track"] song["id"] = track["trackId"] else: song = song_map[track["trackId"]] self.playlists[plist_name].append(song) #------------------------------------------------------------------------------ def get_stream_url(self, song): return self.__api.get_stream_url(song["id"], self.__device_id) #------------------------------------------------------------------------------ def rate_song(self, song, rating): try: song["rating"] = rating song_list = [song] self.__api.change_song_metadata(song_list) print "Gave a Thumbs Up to {0} by {1} on Google Play.".format( song["title"].encode("utf-8"), song["artist"].encode("utf-8")) except RuntimeError: print "Error giving a Thumbs Up on Google Play."
if len(pl) == 1: playlist = pl[0] print 'Analysing playlist: %s' % playlist['name'] if not playlist: print 'Playlist ID or name are required.' for p in pl: print '%20s - %s' % (p['id'], p['name']) sys.exit(1) if 'tracks' not in playlist: print 'No tracks found!' sys.exit(1) songs = mc.get_all_songs() titles = {x['id']: x for x in songs} for t in playlist['tracks']: if 'track' not in t: info = titles[t['trackId']] print ">>> %31.29s / %.20s / \"%.30s\"" % (info['album'], info['artist'], info['title']) else: info = t['track'] print "%35.33s / %.20s / \"%.30s\"" % (info['album'], info['artist'], info['title']) missing = [] for t in playlist['tracks']: if 'track' not in t: info = titles[t['trackId']] if 'nid' not in t or t['nid'][0] != 'T':
#Create the mobile client #No debug logging, validate, and verify SSL api = Mobileclient(False,True,True) #Login #api.perform_oauth('C:\\Users\\Joshv\\Josh\\Programming\\Python\\googleMusic\\gMusicDeviceID.txt') api.oauth_login('b83fce595c6fd82bcfcfdd8e2e542d79f3c176bb0974c4bb34da2df10d95cec1') #Retrieve all playlists playlists = api.get_all_playlists(False, False) #Retrive the contents of all playlists playlistContents = api.get_all_user_playlist_contents() #Get the entire song library library = api.get_all_songs(False, False) #Loop through all of the local playlists for playlistFilePath in playlistFilePaths: #Create the array for the paths artists, tracks in the local playlist paths = [] lArtists = [] lTracks = [] #Create arrays for the artists and tracks in the gmusic playlist gArtists = [] gTracks = [] #Get the playlist file name playlistFileName = os.path.basename(playlistFilePath) playlistName = os.path.splitext(playlistFileName)[0]
class MusicLibrary(object): """This class reads information about your Google Play Music library""" def __init__(self, username=None, password=None, true_file_size=False, scan=True, verbose=0): self.verbose = False if verbose > 1: self.verbose = True self.__login_and_setup(username, password) self.__artists = {} # 'artist name' -> {'album name' : Album(), ...} self.__gartists = {} self.__albums = [] # [Album(), ...] self.__galbums = {} self.__tracks = {} self.__playlists = {} if scan: self.rescan() self.true_file_size = true_file_size def rescan(self): """Scan the Google Play Music library""" self.__artists = {} # 'artist name' -> {'album name' : Album(), ...} self.__gartists = {} self.__albums = [] # [Album(), ...] self.__galbums = {} self.__tracks = {} self.__playlists = {} self.__aggregate_albums() def __login_and_setup(self, username=None, password=None): # If credentials are not specified, get them from $HOME/.gmusicfs if not username or not password: cred_path = os.path.join(os.path.expanduser('~'), '.gmusicfs') if not os.path.isfile(cred_path): raise NoCredentialException( 'No username/password was specified. No config file could ' 'be found either. Try creating %s and specifying your ' 'username/password there. Make sure to chmod 600.' % cred_path) if not oct(os.stat(cred_path)[os.path.stat.ST_MODE]).endswith('00'): raise NoCredentialException( 'Config file is not protected. Please run: ' 'chmod 600 %s' % cred_path) self.config = ConfigParser.ConfigParser() self.config.read(cred_path) username = self.config.get('credentials', 'username') password = self.config.get('credentials', 'password') global deviceId deviceId = self.config.get('credentials', 'deviceId') if not username or not password: raise NoCredentialException( 'No username/password could be read from config file' ': %s' % cred_path) if not deviceId: raise NoCredentialException( 'No deviceId could be read from config file' ': %s' % cred_path) if deviceId.startswith("0x"): deviceId = deviceId[2:] self.api = GoogleMusicAPI(debug_logging=self.verbose) log.info('Logging in...') self.api.login(username, password, deviceId) log.info('Login successful.') def __set_key_from_ginfo(self, track, ginfo, key, to_key=None): """Set track key from either album_info or artist_info""" if to_key is None: to_key = key try: int_key = int(key) except ValueError: int_key = None if (not track.has_key(key) or track[key] == "" or int_key == 0) and ginfo.has_key(to_key): track[key] = ginfo[to_key] return track def __aggregate_albums(self): """Get all the tracks and playlists in the library, parse into relevant dicts""" log.info('Gathering track information...') tracks = self.api.get_all_songs() for track in tracks: log.debug('track = %s' % pp.pformat(track)) # Get album and artist information from Google if track.has_key('albumId'): if self.__galbums.has_key(track['albumId']): album_info = self.__galbums[track['albumId']] else: log.info("Downloading album info for %s '%s'", track['albumId'], track['album']) try: album_info = self.__galbums[track['albumId']] = self.api.get_album_info(track['albumId'], include_tracks=False) except gmusicapi.exceptions.CallFailure: log.exception("Failed to download album info for %s '%s'", track['albumId'], track['album']) #album_info = {} if album_info.has_key('artistId') and len(album_info['artistId']) > 0 and album_info['artistId'][0] != "": artist_id = album_info['artistId'][0] if self.__gartists.has_key(artist_id): artist_info = self.__gartists[artist_id] else: log.info("Downloading artist info for %s '%s'", artist_id, album_info['albumArtist']) #if album_info['albumArtist'] == "Various": # print album_info artist_info = self.__gartists[artist_id] = self.api.get_artist_info(artist_id, include_albums=False, max_top_tracks=0, max_rel_artist=0) else: artist_info = {} else: album_info = {} artist_info = {} track = self.__set_key_from_ginfo(track, album_info, 'album', 'name') track = self.__set_key_from_ginfo(track, album_info, 'year') track = self.__set_key_from_ginfo(track, artist_info, 'albumArtist', 'name') # Prefer the album artist over the track artist if there is one artist_name = formatNames(track['albumArtist']) if artist_name.strip() == '': artist_name = formatNames(track['artist']) if artist_name.strip() == '': artist_name = 'Unknown' # Get the Artist object, or create one if it doesn't exist artist = self.__artists.get(artist_name.lower(), None) if not artist: artist = Artist(self, artist_name) self.__artists[artist_name.lower()] = artist # Get the Album object, or create one if it doesn't exist album = artist.get_album(formatNames(track['album'])) if not album: album = Album(self, track['album']) self.__albums.append(album) # NOTE: Current no purpose other than to count artist.add_album(album) # Add track to album album.add_track(track) # Add track to list of all tracks, indexable by track ID if 'id' in track: self.__tracks[track['id']] = track log.info('%d tracks loaded.' % len(tracks)) log.info('%d artists loaded.' % len(self.__artists)) log.info('%d albums loaded.' % len(self.__albums)) # Add all playlists playlists = self.api.get_all_user_playlist_contents() for pldata in playlists: playlist = Playlist(self, pldata) self.__playlists[playlist.dirname.lower()] = playlist log.debug('%d playlists loaded.' % len(self.__playlists)) def get_artists(self): """Return all artists in the library""" return self.__artists def get_artist(self, name): """Return the artist from the library with the specified name""" return self.__artists.get(name.lower(), None) def get_playlists(self): """Return list of all playlists in the library""" return self.__playlists.values() def get_playlist(self, name): """Return the playlist from the library with the specified name""" return self.__playlists.get(name.lower(), None) def get_track(self, trackid): """Return the track from the library with the specified track ID""" return self.__tracks.get(trackid, None) def cleanup(self): pass
def main(): log.setLevel(logging.INFO) logging.getLogger('gmusicapi').setLevel(logging.INFO) cred_path = os.path.join(os.path.expanduser('~'), '.gmusic-sync') if not os.path.isfile(cred_path): raise NoCredentialException( 'No username/password was specified. No config file could ' 'be found either. Try creating %s and specifying your ' 'username/password there. Make sure to chmod 600.' % cred_path) if not oct(os.stat(cred_path)[os.path.stat.ST_MODE]).endswith('00'): raise NoCredentialException( 'Config file is not protected. Please run: ' 'chmod 600 %s' % cred_path) config = ConfigParser.ConfigParser() config.read(cred_path) src_user = config.get('src','username') src_pass = config.get('src','password') src_device = config.get('src','deviceid') dst_user = config.get('dst','username') dst_pass = config.get('dst','password') dst_device = config.get('dst','deviceid') if not src_user or not src_pass or not dst_user or not dst_pass: raise NoCredentialException( 'No username/password could be read from config file' ': %s' % cred_path) if not src_device or not dst_device: raise NoCredentialException( 'No deviceId could be read from config file' ': %s' % cred_path) parser = argparse.ArgumentParser(description='gmusic-sync', add_help=False) parser.add_argument('-hs', '--strict-heuristics', help='Songs must match artist, album, and title to be considered a match.', action='store_true', dest='strict_heuristics') parser.add_argument('-e', '--exact', help='Copy the exact rating, instead of upgrading from 1-5 star ratings to Thumbs Up/Down', action='store_true', dest='exact') args = parser.parse_args() api = Mobileclient() api.login(src_user, src_pass, src_device) library = api.get_all_songs() api2 = Mobileclient() api2.login(dst_user, dst_pass, dst_device) library2 = api2.get_all_songs() failed_tracks = [] rated_tracks = [] #first, get all tracks in the library with a rating for track in library: try: if track['rating'] != '0' and track['lastRatingChangeTimestamp'] != '0': rated_tracks.append(track) except: #print 'ERROR: track did not contain rating key: ' + track_info_str(track) pass #sort the tracks by rating date rated_tracks.sort(key=operator.itemgetter('lastRatingChangeTimestamp')) for track in rated_tracks: print track_info_str(track) print 'TOTAL RATED TRACKS: ' + str(len(rated_tracks)) for track in rated_tracks: try: if track['storeId'].startswith('T'): #It's a store track: does it exist in the target store? #Perform a store lookup: this will raise an exception if the track #Is not in the target store dst_track = api2.get_track_info(track['storeId']) #If we got here, the song is ready to be rated transfer_rating(api2, track, dst_track, args.exact) else: dst_track = heuristic_search(library2, track, args.strict_heuristics) if dst_track is not None: transfer_rating(api2, track, dst_track, args.exact) else: failed_tracks.append(track) except: #Not a store track: do heuristics lookup dst_track = heuristic_search(library2, track, args.strict_heuristics) if dst_track is not None: transfer_rating(api2, track, dst_track, args.exact) else: failed_tracks.append(track) #Absolutely must wait between ratings or we won't get valid timestamps time.sleep(2) print '----------------- FAILED TRACKS --------------------' for track in failed_tracks: print track_info_str(track)
#!/usr/bin/env python from gmusicapi import Mobileclient from getpass import getpass client = Mobileclient() client.login( raw_input( "Username: "******"Getting all songs ..." all_songs = client.get_all_songs() new_songs = {} old_songs = {} for song in all_songs: song_id = song.get('id') timestamp = song.get('recentTimestamp') key = "%s: %d-%02d %s" % ( song.get('album'), song.get('discNumber'), song.get('trackNumber'), song.get('title') ) if key in new_songs: if new_songs[key]['timestamp'] < timestamp: old_songs[key] = new_songs[key] new_songs[key] = { 'id': song_id, 'timestamp': timestamp } else: old_songs[key] = { 'id': song_id, 'timestamp': timestamp } new_songs[key] = { 'id': song_id, 'timestamp': timestamp } if len( old_songs ): print "Duplicate songs"
class GMusicWrapper(object): def __init__(self, username, password, logger=None): self._api = Mobileclient() self.logger = logger success = self._api.login( username, password, getenv('ANDROID_ID', Mobileclient.FROM_MAC_ADDRESS)) if not success: raise Exception("Unsuccessful login. Aborting!") # Populate our library self.library = {} self.indexing_thread = threading.Thread(target=self.index_library) self.indexing_thread.start() def log(self, log_str): self.logger.debug(log_str) def _search(self, query_type, query): try: results = self._api.search(query) except CallFailure: return [] hits_key = "%s_hits" % query_type if hits_key not in results: return [] # Ugh, Google had to make this schema nonstandard... if query_type == 'song': query_type = 'track' return [x[query_type] for x in results[hits_key]] def is_indexing(self): return self.indexing_thread.is_alive() def index_library(self): """ Downloads the a list of every track in a user's library and populates self.library with storeIds -> track definitions """ self.log('Fetching library...') tracks = self.get_all_songs() for track in tracks: song_id = track['id'] self.library[song_id] = track self.log('Fetching library...') def get_artist(self, name): """ Fetches information about an artist given its name """ search = self._search("artist", name) if len(search) == 0: return False return self._api.get_artist_info(search[0]['artistId'], max_top_tracks=100) def get_album(self, name, artist_name=None): if artist_name: name = "%s %s" % (name, artist_name) search = self._search("album", name) if len(search) == 0: return False return self._api.get_album_info(search[0]['albumId']) def get_latest_album(self, artist_name=None): search = self._search("artist", artist_name) if len(search) == 0: return False artist_info = self._api.get_artist_info(search[0]['artistId'], include_albums=True) album_info = artist_info['albums'] sorted_list = sorted(album_info.__iter__(), key=lambda s: s['year'], reverse=True) for index, val in enumerate(sorted_list): album_info = self._api.get_album_info( album_id=sorted_list[index]['albumId'], include_tracks=True) if len(album_info['tracks']) >= 5: return album_info return False def get_album_by_artist(self, artist_name, album_id=None): search = self._search("artist", artist_name) if len(search) == 0: return False artist_info = self._api.get_artist_info(search[0]['artistId'], include_albums=True) album_info = artist_info['albums'] random.shuffle(album_info) for index, val in enumerate(album_info): album = self._api.get_album_info( album_id=album_info[index]['albumId'], include_tracks=True) if album['albumId'] != album_id and len(album['tracks']) >= 5: return album return False def get_song(self, name, artist_name=None, album_name=None): if artist_name: name = "%s %s" % (artist_name, name) elif album_name: name = "%s %s" % (album_name, name) search = self._search("song", name) if len(search) == 0: return False if album_name: for i in range(0, len(search) - 1): if album_name in search[i]['album']: return search[i] return search[0] def get_promoted_songs(self): return self._api.get_promoted_songs() def get_station(self, title, track_id=None, artist_id=None, album_id=None): if artist_id is not None: if album_id is not None: if track_id is not None: return self._api.create_station(title, track_id=track_id) return self._api.create_station(title, album_id=album_id) return self._api.create_station(title, artist_id=artist_id) def get_station_tracks(self, station_id): return self._api.get_station_tracks(station_id) def get_google_stream_url(self, song_id): return self._api.get_stream_url(song_id) def get_stream_url(self, song_id): return "%s/alexa/stream/%s" % (getenv('APP_URL'), song_id) def get_thumbnail(self, artist_art): return artist_art.replace("http://", "https://") def get_all_user_playlist_contents(self): return self._api.get_all_user_playlist_contents() def get_all_songs(self): return self._api.get_all_songs() def rate_song(self, song, rating): return self._api.rate_songs(song, rating) def extract_track_info(self, track): # When coming from a playlist, track info is nested under the "track" # key if 'track' in track: track = track['track'] if 'storeId' in track: return track, track['storeId'] elif 'trackId' in track: return self.library[track['trackId']], track['trackId'] else: return None, None def get_artist_album_list(self, artist_name): search = self._search("artist", artist_name) if len(search) == 0: return "Unable to find the artist you requested." artist_info = self._api.get_artist_info(search[0]['artistId'], include_albums=True) album_list_text = "Here's the album listing for %s: " % artist_name counter = 0 for index, val in enumerate(artist_info['albums']): if counter > 25: # alexa will time out after 10 seconds if the list takes too long to iterate through break album_info = self._api.get_album_info( album_id=artist_info['albums'][index]['albumId'], include_tracks=True) if len(album_info['tracks']) > 5: counter += 1 album_list_text += ( artist_info['albums'][index]['name']) + ", " return album_list_text def get_latest_artist_albums(self, artist_name): search = self._search("artist", artist_name) if len(search) == 0: return False artist_info = self._api.get_artist_info(search[0]['artistId'], include_albums=True) album_list = artist_info['albums'] sorted_list = sorted(album_list.__iter__(), key=lambda s: s['year'], reverse=True) speech_text = 'The latest albums by %s are ' % artist_name counter = 0 for index, val in enumerate(sorted_list): if counter > 5: break else: album_info = self._api.get_album_info( album_id=sorted_list[index]['albumId'], include_tracks=True) if len(album_info['tracks']) >= 5: counter += 1 album_name = sorted_list[index]['name'] album_year = sorted_list[index]['year'] speech_text += '%s, released in %d, ' % (album_name, album_year) return speech_text def closest_match(self, request_name, all_matches, artist_name='', minimum_score=70): # Give each match a score based on its similarity to the requested # name self.log('Fetching library...') request_name = request_name.lower() + artist_name.lower() scored_matches = [] for i, match in enumerate(all_matches): try: name = match['name'].lower() except (KeyError, TypeError): i = match name = all_matches[match]['title'].lower() if artist_name != "": name += all_matches[match]['artist'].lower() scored_matches.append({ 'index': i, 'name': name, 'score': fuzz.ratio(name, request_name) }) sorted_matches = sorted(scored_matches, key=lambda a: a['score'], reverse=True) top_scoring = sorted_matches[0] self.log('Fetching library...') best_match = all_matches[top_scoring['index']] # Make sure we have a decent match (the score is n where 0 <= n <= 100) if top_scoring['score'] < minimum_score: return None return best_match def get_genres(self, parent_genre_id=None): return self._api.get_genres(parent_genre_id) def increment_song_playcount(self, song_id, plays=1, playtime=None): return self._api.increment_song_playcount(song_id, plays, playtime) def get_song_data(self, song_id): return self._api.get_track_info(song_id) @classmethod def generate_api(cls, **kwargs): return cls(getenv('GOOGLE_EMAIL'), getenv('GOOGLE_PASSWORD'), **kwargs)
f = open('foobar.csv', 'r') rows = [] for row in unicodecsv.DictReader(f): rows.append(row) f.close() log("##### Login Google Music #####") # Login into Google Music api = Mobileclient() api.login(gmusic_username, gmusic_password) log("##### Fetching Library #####") library = api.get_all_songs() # Search all songs songs = [] missing = [] log("##### Matching Songs #####") for row in rows: found = False for song in library: if decode(row['title']) == song['title'] and \ decode(row['artist']) == song['artist'] and \ s_in_s(decode(row['album']), song['album']) and\ int(row['tracknum']) == song['trackNumber']: log("Song " + decode(row['title']) + " Found. ID: " + song['id'])
class MusicLibrary(object): 'Read information about your Google Music library' def __init__(self, username=None, password=None, true_file_size=False, scan=True, verbose=0): self.verbose = False if verbose > 1: self.verbose = True self.__login_and_setup(username, password) self.__artists = {} # 'artist name' -> {'album name' : Album(), ...} self.__galbums = {} self.__gartists = {} self.__albums = [] # [Album(), ...] if scan: self.rescan() self.true_file_size = true_file_size def rescan(self): self.__artists = {} # 'artist name' -> {'album name' : Album(), ...} self.__albums = [] # [Album(), ...] self.__galbums = {} self.__gartists = {} self.__aggregate_albums() def __login_and_setup(self, username=None, password=None): # If credentials are not specified, get them from $HOME/.gmusicfs if not username or not password: cred_path = os.path.join(os.path.expanduser('~'), '.gmusicfs') if not os.path.isfile(cred_path): raise NoCredentialException( 'No username/password was specified. No config file could ' 'be found either. Try creating %s and specifying your ' 'username/password there. Make sure to chmod 600.' % cred_path) if not oct(os.stat(cred_path)[os.path.stat.ST_MODE]).endswith('00'): raise NoCredentialException( 'Config file is not protected. Please run: ' 'chmod 600 %s' % cred_path) self.config = ConfigParser.ConfigParser() self.config.read(cred_path) username = self.config.get('credentials','username') password = self.config.get('credentials','password') global deviceId deviceId = self.config.get('credentials','deviceId') if not username or not password: raise NoCredentialException( 'No username/password could be read from config file' ': %s' % cred_path) if not deviceId: raise NoCredentialException( 'No deviceId could be read from config file' ': %s' % cred_path) self.api = GoogleMusicAPI(debug_logging=self.verbose) log.info('Logging in...') self.api.login(username, password) log.info('Login successful.') def __set_key_from_ginfo(self, track, ginfo, key, to_key=None): 'Set track key from either album_info or artist_info' if to_key is None: to_key = key try: int_key = int(key) except ValueError: int_key = None if (not track.has_key(key) or track[key] == "" or int_key == 0) and ginfo.has_key(to_key): track[key] = ginfo[to_key] return track def __cleanup_artist(self, artist): if artist.startswith("featuring"): artist = artist[len("featuring"):].strip() if artist.startswith("feat"): artist = artist[len("feat"):].strip() return artist def __cleanup_name(self, name, track): for bracket in (('\[', '\]'), ('\{', '\}'), ('\(', '\)')): # Remove (xxx Album Version) from track names match = re.compile('^(?P<name>(.*))([ ]+[%s-]([^%s]*)[Vv]ersion[%s]?[ ]*)$' % (bracket[0], bracket[1], bracket[1])).match(name) if match is not None: name = match.groupdict()['name'] name, track = self.__cleanup_name(name, track) # Pull (feat. <artist>) out of name and add to artist list match = re.compile('^(?P<name>(.*))([ ]+[%s][ ]*[Ff]eat[\.]?[ ]*(?P<artist>(.*))[%s]+)(?P<postfix>(.*))$' % (bracket[0], bracket[1])).match(name) if match is not None: name = match.groupdict()['name'] artist = match.groupdict()['artist'] if match.groupdict().has_key('postfix') and match.groupdict()['postfix'] is not None: name += match.groupdict()['postfix'] artist = artist.strip() if artist[-1] in ")}]": # I hate regex's. The one above doesn't catch the last parenthesis if there's one artist = artist[:-1] if artist.find(" and ") > -1 or artist.find(" & ") > -1: artist = artist.replace(', ', ';') artist = artist.replace(' & ', ';') artist = artist.replace(' and ', ';') alist = artist.split(';') for artist in alist: track['artist'].append(artist.strip()) name, track = self.__cleanup_name(name, track) # Remove () or ( ) from track names match = re.compile('^(?P<name>(.*))([ ]*[%s][ ]?[%s][ ]*)$' % (bracket[0], bracket[1])).match(name) if match is not None: name = match.groupdict()['name'] name, track = self.__cleanup_name(name, track) # Strip any extra whitespace from the name name = name.strip() return name, track def __cleanup_track(self, track): name = track['title'] name, track = self.__cleanup_name(name, track) track['title'] = name for anum in range(0, len(track['artist'])): track['artist'][anum] = self.__cleanup_artist(track['artist'][anum]) return track def __aggregate_albums(self): 'Get all the tracks in the library, parse into artist and album dicts' all_artist_albums = {} log.info('Gathering track information...') tracks = self.api.get_all_songs() for track in tracks: if track.has_key('artist'): if track['artist'].find(" and ") > -1 or track['artist'].find(" & ") > -1: track['artist'] = track['artist'].replace(', ', ';') track['artist'] = track['artist'].replace(' & ', ';') track['artist'] = track['artist'].replace(' and ', ';') track['artist'] = track['artist'].split(';') else: track['artist'] = [] track = self.__cleanup_track(track) if track.has_key('albumArtist') and track['albumArtist'] != "": albumartist = track['albumArtist'] elif len(track['artist']) == 1 and track['artist'][0] != "": albumartist = track['artist'][0] else: albumartist = "Unknown" # Get album and artist information from Google if track.has_key('albumId'): if self.__galbums.has_key(track['albumId']): album_info = self.__galbums[track['albumId']] else: print "Downloading album info for '%s'" % track['album'] album_info = self.__galbums[track['albumId']] = self.api.get_album_info(track['albumId'], include_tracks=False) if album_info.has_key('artistId') and len(album_info['artistId']) > 0 and album_info['artistId'][0] != "": artist_id = album_info['artistId'][0] if self.__gartists.has_key(artist_id): artist_info = self.__gartists[artist_id] else: print "Downloading artist info for '%s'" % album_info['albumArtist'] if album_info['albumArtist'] == "Various": print album_info artist_info = self.__gartists[artist_id] = self.api.get_artist_info(artist_id, include_albums=False, max_top_tracks=0, max_rel_artist=0) else: artist_info = {} else: album_info = {} artist_info = {} track = self.__set_key_from_ginfo(track, album_info, 'album', 'name') track = self.__set_key_from_ginfo(track, album_info, 'year') track = self.__set_key_from_ginfo(track, artist_info, 'albumArtist', 'name') # Fix for odd capitalization issues if artist_info.has_key('name') and track['albumArtist'].lower() == artist_info['name'].lower() and track['albumArtist'] != artist_info['name']: track['albumArtist'] = artist_info['name'] for anum in range(0, len(track['artist'])): if artist_info.has_key('name') and track['artist'][anum].lower() == artist_info['name'].lower() and track['artist'][anum] != artist_info['name']: track['artist'][anum] = artist_info['name'] if not track.has_key('albumId'): track['albumKey'] = "%s|||%s" % (albumartist, track['album']) else: track['albumKey'] = track['albumId'] album = all_artist_albums.get(track['albumKey'], None) if not album: album = all_artist_albums[track['albumKey']] = Album( self, formatNames(track['album']), track['albumArtist'], track['album'], track['year'] ) self.__albums.append(album) artist_albums = self.__artists.get(track['albumArtist'], None) if artist_albums: artist_albums[formatNames(album.normtitle)] = album else: self.__artists[track['albumArtist']] = {album.normtitle: album} artist_albums = self.__artists[track['albumArtist']] album.add_track(track) # Separate multi-disc albums for artist in self.__artists.values(): for key in artist.keys(): album = artist[key] if album.get_disc_count() > 1: for d in album.get_discs(): new_name = "%s - Disc %i" % (album.album, d) new_album = Album(album.library, formatNames(new_name), album.artist, new_name, album.year) album.copy_art_to(new_album) new_album.show_discnum = True new_key = None for t in album.get_tracks(): if int(t['discNumber']) == d: new_album.add_track(t) artist[formatNames(new_name)] = new_album del artist[key] log.debug('%d tracks loaded.' % len(tracks)) log.debug('%d artists loaded.' % len(self.__artists)) log.debug('%d albums loaded.' % len(self.__albums)) def get_artists(self): return self.__artists def get_albums(self): return self.__albums def get_artist_albums(self, artist): log.debug(artist) return self.__artists[artist] def cleanup(self): pass
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
break except: print("oops, this didn't work, trying again") i += 1 i %= len(ids) else: oa = api.perform_oauth() api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, oauth_credentials=oa) devices = api.get_registered_devices() ids = [] for i in devices: ids.append(i['id'][2:]) with open('config.json', 'w') as fp: json.dump(ids, fp) songs = api.get_all_songs() for song in songs: dirName = normalizePath("%s - %s" % (song["artist"], song["album"])) dirPath = targetDir + "/" + dirName if not os.path.exists(dirPath): print("downloading to directory: " + dirPath) os.makedirs(dirPath) fileName = normalizePath( "%s. %s - %s.mp3" % (song["trackNumber"], song["artist"], song["title"])) filePath = dirPath + "/" + fileName if os.path.exists(filePath): print(fileName + " already exists, skipping") continue url = api.get_stream_url(song_id=song["id"], quality="hi") print("downloading: " + fileName)
if 'album' in track and 'title' in track: track_txt_key = track['album'] + '_' + track['title']; track_txt_key = track_txt_key.lower() if track_txt_key not in uniq_tracks: uniq_tracks.append(track_txt_key) else: duplicatesTracks.append(track) return duplicatesTracks api = Mobileclient() logged_in = api.login('login', 'password') if logged_in: print "Successfully logged in. Beginning duplication removal process." all_tracks = api.get_all_songs() print "all tracks: ", len(all_tracks) duplicate_tracks = getDuplicatesSongInAlbums(all_tracks) print "duplication tracks count: %s" % len(duplicate_tracks) show_tracks = raw_input("Show list tracks to delete?:[y] Y/n ") if show_tracks.lower() == 'y' or show_tracks == '': for track in duplicate_tracks: name = u"%s - %s - %s" % (track['artist'], track['album'], track['title']) print name.encode('utf8') if len(duplicate_tracks) > 0: deleteConfirnation = raw_input("Are you sure you want to delete tracks (%d)? [y] Y/n" % len(duplicate_tracks)) if deleteConfirnation.lower() == 'y' or deleteConfirnation == '': duplicate_track_ids = get_track_ids(duplicate_tracks) deleted_track_ids = api.delete_songs(duplicate_track_ids) print "Successfully deleted " + str(len(deleted_track_ids)) + " of " + str(len(duplicate_track_ids)) + " queued songs for removal." else:
if __name__ == "__main__": manager = GPMDBManager() """ Load all songs into db (Use ONLY ONCE) """ ANDROID_DEVICE_MAC_ADDRESS = "00:00:00:00:00:00" client = Mobileclient() client.login("*****@*****.**", "xyz", ANDROID_DEVICE_MAC_ADDRESS) print("Getting songs") library = client.get_all_songs() print("Retreived all songs") for i, songDict in enumerate(library): name = songDict["title"] album = songDict["album"] artist = songDict["artist"] duration = songDict["durationMillis"] playCount = songDict["playCount"] songRating = songDict["rating"] songComposer = songDict["composer"] songYear = songDict["year"] manager.insertSong(name, album, artist, duration, playCount, songRating, songComposer, songYear) print("Inserted Song %d : Name = %s" % ((i+1), name))
class GMusic(object): def __init__(self): self.mob_client = Mobileclient() self.web_client = Webclient() self.logfile = None self.logfile_open = False # logged_in is True if login was successful logged_in = self.mob_client.login(MY_GMAIL, MY_PASSWORD, Mobileclient.FROM_MAC_ADDRESS) if logged_in: print("GoogleMusic MobileClient Logged In") else: raise Exception("Couldn't log in, exiting...") logged_in = self.web_client.login(MY_GMAIL, MY_PASSWORD) if logged_in: print("GoogleMusic WebClient Logged In") else: raise Exception("Couldn't log in, exiting...") def build_play_list_dummy(self): library = self.mob_client.get_all_songs() tracks = [track for track in library if track['artist'] == 'Adhesive Wombat' and "night shade" in track['title'].lower()] # for track in sweet_tracks: # print(track) playlist_id = self.mob_client.create_playlist('Test playlist') for track in tracks: self.mob_client.add_songs_to_playlist(playlist_id, track['id']) return playlist_id def _setlogfile(self, logfile): if self.logfile_open: self._print_and_log("logfile {} already opened! Not opening again!") else: self.logfile = logfile with open(self.logfile, "w") as logfile: logfile.write("LOGSTART: {}, script: {}\n".format(asctime(localtime()), __file__)) self.logfile_open = True def _print_and_log(self, msg): if self.logfile: with open(self.logfile, "a") as logfile: logfile.write(msg+"\n") print msg def find_duplicate_songs(self, outfile=None): duplicates = [] if outfile: if path.exists(path.dirname(outfile)): self._setlogfile(outfile) else: raise IOError("Output filename given: {} is in an none-existing dir".format(outfile)) library = self.mob_client.get_all_songs() tracks = [track for track in library] while tracks: track = tracks[0] dup_list = [] dup_idx_list = [] for idx, track_i in enumerate(tracks): if track['artist'].lower() == track_i['artist'].lower() and\ track['album'].lower() == track_i['album'].lower() and\ track['discNumber'] == track_i['discNumber'] and\ track['trackNumber'] == track_i['trackNumber'] and\ track['title'].lower() == track_i['title'].lower(): dup_idx_list.append(idx) dup_list.append(track_i) # Remove them: for index in sorted(dup_idx_list, reverse=True): del tracks[index] if len(dup_list) > 1: duplicates.append(dup_list) for idx, dup_list in enumerate(duplicates): self._print_and_log("{}: '{}' was found {} times!".format(idx+1, dup_list[0]['title'].encode("utf-8"), len(dup_list))) self._print_and_log("Found a total of {} duplicate songs!".format(len(duplicates))) # Display important stuff for idx, track_list in enumerate(duplicates): self._print_and_log("{}: BAND: {}, NAME: '{}'".format(idx+1, track_list[0]['artist'], track_list[0]['title'].encode("utf-8"))) for el in track_list[0]: for track in track_list: if el not in track: track[el] = "NO VALUE" if track[el] != track_list[0][el] and el not in ['id', 'url', 'recentTimestamp', 'storeId', 'nid', 'clientId']: # unicode? try: r_val = track_list[0][el].encode("utf-8") except: r_val = track_list[0][el] # unicode? try: l_val = track[el].encode("utf-8") except: l_val = track[el] self._print_and_log("track_id {}: {}='{}'".format(track_list[0]['id'], el, r_val)) self._print_and_log("track_id {}: {}='{}'".format(track['id'], el, l_val)) # raw_input("Press any key to continue...") return duplicates def delete_duplicates(self, duplicates): self._print_and_log("Cleaning duplicates [removing oldest of each duplicant]:") old_song_ids = [] for idx, dup_list in enumerate(duplicates): self._print_and_log("{}: BAND: {}, NAME: '{}'".format(idx+1, dup_list[0]['artist'], dup_list[0]['title'].encode("utf-8"))) track_timstamp = None oldest_id = None for el in dup_list: if track_timstamp is None and oldest_id is None: track_timstamp = el["timestamp"] oldest_id = el["id"] elif el["timestamp"] < track_timstamp: track_timstamp = el["timestamp"] oldest_id = el["id"] # finished with dup_list - log oldest id: self._print_and_log("Will delete {}, track_id: {}".format(el["title"], el["id"])) old_song_ids.append(oldest_id) self.mob_client.delete_songs(old_song_ids) self._print_and_log("track_ids deleted:\n{}".format(old_song_ids))