def main(): args, config = init() if not os.path.isdir(config['PROG']['DownloadPath']): os.mkdir(config['PROG']['DownloadPath']) sc_download( config['SOUNDCLOUD']['PlaylistUrl'], config['PROG']['DownloadPath'] ) mc = Mobileclient() mc.login(config['GMUSIC']['User'], config['GMUSIC']['Password'], Mobileclient.FROM_MAC_ADDRESS) mm = Musicmanager() if not (os.path.exists(gmclients.OAUTH_FILEPATH) and mm.login(gmclients.OAUTH_FILEPATH)): mm.perform_oauth(gmclients.OAUTH_FILEPATH, open_browser=True) if not mm.login(gmclients.OAUTH_FILEPATH): sys.stderr.write('Musicmanager could not authenticate') if config['GMUSIC']['TargetPlaylist'] not in set([item['name'] for item in mc.get_all_playlists() if not item['deleted']]): mc.create_playlist(name=config['GMUSIC']['TargetPlaylist'], description='Tracks synchronized using {}'.format(__prog__), public=False) playlist_id, current_members = gm_get_current_pl_member(mc, config['GMUSIC']['TargetPlaylist']) for track in os.listdir(config['PROG']['DownloadPath']): print('Uploading {}'.format(track)) uploaded_id = gm_extract_id(mm.upload('{}{}'.format(config['PROG']['DownloadPath'], track))) mc.add_songs_to_playlist(playlist_id, uploaded_id)
class Playlist(): ''' Parent class for specific types of playlists, like: Live Setlist Playlist setlist based on the set of songs played at a live concert Upcoming Concert Playlist setlist generated from a list of bands that have upcoming concerts in an area ''' def __init__(self, make=False): self.setup_logging() self.api = Mobileclient() self.logged_in = self.api.login( EMAIL, TOKEN, # Mobileclient.FROM_MAC_ADDRESS) DEVICE_ID) if self.logged_in: self.info("Logged into GPMAA successfully") def setup_logging(self): logger_name = '.'.join([__name__, __class__.__name__]) self.logger = logging.getLogger(logger_name) logging.getLogger('gmusicapi.protocol.shared').setLevel(logging.INFO) logging.getLogger('urllib3.connectionpool').setLevel(logging.WARNING) def error(self, msg): self.logger.error(msg) def info(self, msg): self.logger.info(msg) def debug(self, msg): self.logger.debug(msg) def search(self, query): ''' This function got pulled to the parent class because we'll always be searching for things and wanting the song results. They're always going to have to be processed this way because of how the search result is formatted. The result is a list of song dictionaries with keys such as storeId, artist, etc. ''' res = self.api.search(query)['song_hits'] res = [song['track'] for song in res] return res def create_playlist(self, song_ids, name, description='', public=False): self.info("Creating {}".format(name)) self.id = self.api.create_playlist(name, description, public) self.api.add_songs_to_playlist(self.id, song_ids) def delete_playlist(self): if hasattr(self, 'id') and self.id is not None: self.info("Deleting playlist id: %s".format(self.id)) self.api.delete_playlist(self.id) else: self.info("Can't delete a playlist without its id")
class GMusicAPI(): def __init__(self, username=None, encrypted_pass=None): self._api = Mobileclient() self.logged_in = False if username and encrypted_pass: self.login(username, encrypted_pass) def login(self, username, encrypted_pass): self.logged_in = self._api.login(username, decrypt(encrypted_pass), Mobileclient.FROM_MAC_ADDRESS) def logout(self): self._api.logout() self.logged_in = False def clear_playlist(self, playlist_name): playlists = self._api.get_all_user_playlist_contents() playlist = [playlist for playlist in playlists if playlist['name'] == playlist_name][0] entry_ids = [entry['id'] for entry in playlist['tracks']] removed = self._api.remove_entries_from_playlist(entry_ids) return len(removed) def search(self, *args): """ Returns the best-fitting track dict for the given information. :param args: Strings which can be artist, song title, album etc. :return: """ query = sanitise_query(' '.join(args)) result = self._api.search(query) song_results = result['song_hits'] if not song_results: warnings.warn('Warning: query {} returned no song hits.'.format(query)) return None tracks = [song_result['track'] for song_result in song_results[:5]] for track in tracks: if not is_tribute(track, query): return track warnings.warn('Warning: query {} returned no non-tribute song hits.'.format(query)) return None def get_playlist_id(self, playlist_name): for playlist in self._api.get_all_playlists(): if playlist['name'] == playlist_name: return playlist['id'] raise ValueError("Playlist '{}' not found".format(playlist_name)) def add_songs(self, playlist_name, tracks): playlist_id = self.get_playlist_id(playlist_name) track_ids = [track['nid'] for track in tracks if track] self._api.add_songs_to_playlist(playlist_id, track_ids)
class GMusicWS: def __init__(self, user, password, playlistName): self.playlistName = playlistName self.api = Mobileclient() print("Logging into MobileClient API") self.api.login(user, password, "android_id") #insert unique android_id here def mapUnknownTracks(self, db): playlist = db.unmappedTracks() for track in playlist: searchstr = track.artist + " " + track.song print("Searching for %s" % (searchstr)) try: result = self.api.search_all_access(searchstr, max_results=1) print("Found " + result['song_hits'][0]['track']['artist'] + " - " + result['song_hits'][0]['track']['title']) nid = result['song_hits'][0]['track']['nid'] db.storemapping(track.song, track.artist, nid) except: print("Error parsing result: " + str(result)) time.sleep(1) def maintain(self, tracks): print("Searching for playlist %s" % (self.playlistName)) found = False searchres = self.api.get_all_playlists() for list in searchres: if list['name'] == self.playlistName: found = True pid = list['id'] if not found: print("Not found - creating") pid = self.api.create_playlist(self.playlistName) print("Playlist id is %s" % (pid)) print("Getting current contents") playlists = self.api.get_all_user_playlist_contents() currentEntries = [] for playlist in playlists: if playlist['name'] == self.playlistName: for entry in playlist['tracks']: currentEntries.append(entry['id']) print("Removing songs") self.api.remove_entries_from_playlist(currentEntries) print("Adding songs") self.api.add_songs_to_playlist(pid, tracks)
def add_songs_to_library(self): api = Mobileclient() logged_in = api.login('*****@*****.**', '1944_D-d@y', Mobileclient.FROM_MAC_ADDRESS) playlist = api.create_playlist("JackFM #2", "This is what Jack is playing next") for sn in crawler.songs: print sn.name search_res = api.search(sn.name + " " + sn.artist, 10) songid = search_res["song_hits"][0]["track"]["storeId"] api.add_songs_to_playlist(playlist, songid)
class GMusicWS: def __init__(self, user, password, playlistName): self.playlistName = playlistName self.api = Mobileclient() print ("Logging into MobileClient API") self.api.login(user, password,"android_id") #insert unique android_id here def mapUnknownTracks(self, db): playlist = db.unmappedTracks() for track in playlist: searchstr = track.artist + " " + track.song print ("Searching for %s" % (searchstr)) try: result = self.api.search_all_access(searchstr, max_results=1) print ("Found " + result['song_hits'][0]['track']['artist'] + " - " + result['song_hits'][0]['track']['title']) nid = result['song_hits'][0]['track']['nid'] db.storemapping(track.song, track.artist, nid) except: print ("Error parsing result: " + str(result)) time.sleep(1) def maintain(self, tracks): print ("Searching for playlist %s" % (self.playlistName)) found = False searchres = self.api.get_all_playlists() for list in searchres: if list['name'] == self.playlistName: found = True pid = list['id'] if not found: print ("Not found - creating") pid = self.api.create_playlist(self.playlistName) print ("Playlist id is %s" % (pid)) print ("Getting current contents") playlists = self.api.get_all_user_playlist_contents() currentEntries = [] for playlist in playlists: if playlist['name'] == self.playlistName: for entry in playlist['tracks']: currentEntries.append(entry['id']) print ("Removing songs") self.api.remove_entries_from_playlist(currentEntries) print ("Adding songs") self.api.add_songs_to_playlist(pid, tracks)
def main(): logger.info('Scanning the Billboard Charts......') chart_df_dict = {} global gmusic if not gmusic: gmusic = Mobileclient(debug_logging=False) gmusic.login(email=USER_NAME, password=APP_PASSWORD, locale='en_US', android_id=Mobileclient.FROM_MAC_ADDRESS) for chart, chart_name in charts_to_playlist: logger.info('Getting {}......'.format(chart_name)) chart_scraped = billboard.ChartData(config['BILLBOARD'][chart]) chart_df = get_song_df(chart_scraped) #Delete Playlist if present. logger.info('Updating {} Playlist......'.format(chart_name)) delete_playlist_if_exists(chart_name) #Create Playlist playlist_id = gmusic.create_playlist( name=chart_name, description=chart_name + ' from Billboard on week {}'.format( current_week(dt.datetime.today())), public=True) gmusic.add_songs_to_playlist( playlist_id=playlist_id, song_ids=chart_df.song_id.dropna().tolist()) chart_df_dict[chart] = chart_df #Top Rising from Hot 100 if chart_df_dict.has_key('HOT_100'): logger.info('Updating {} Playlist......'.format('Top 25 Risers')) top_100 = chart_df_dict['HOT_100'] top_100.loc[top_100.change == 'Hot Shot Debut', 'change'] = 100 top_100.change = pd.to_numeric(top_100.change, errors='coerce').fillna(0) hot25_risers = top_100.sort_values( by=['change'], ascending=False).head(25).sort_values(by=['rank'], ascending=True) delete_playlist_if_exists('Top 25 Risers') playlist_id = gmusic.create_playlist( 'Top 25 Risers', 'Top 25 Risers from Hot 100 Billboard on week {}'.format( current_week(dt.datetime.today()))) gmusic.add_songs_to_playlist( playlist_id=playlist_id, song_ids=hot25_risers.song_id.dropna().tolist()) logger.info('Finished Updating......'.format('Top 25 Risers'))
def export_song_data(name, data, dry_run): gmusic = Mobileclient() logged_in = gmusic.oauth_login(Mobileclient.FROM_MAC_ADDRESS) if not logged_in: print("Couldn't log in. Starting authentication...") gmusic.perform_oauth() logged_in = gmusic.oauth_login(Mobileclient.FROM_MAC_ADDRESS) if logged_in: print("Logged in!") else: print("Login failed.") sys.exit(1) song_ids = data_to_song_ids(gmusic, data) print("Found %d/%d tracks on Google Music." % (len(song_ids), len(data))) if song_ids and not dry_run: playlist_id = gmusic.create_playlist(name) gmusic.add_songs_to_playlist(playlist_id, song_ids) print("Created playlist \"%s\"." % name)
class GMusicPodcastSyncDestination(PodcastSyncDestination): def __init__(self): super(GMusicPodcastSyncDestination, self).__init__() self.serviceIdentifier = "GOO" self.serviceName = "Google Music" self.musicManager = Musicmanager() self.mobileClient = Mobileclient() oAuthFile = "gMusic.oauth" if not os.path.isfile(oAuthFile): if not self.musicManager.perform_oauth(oAuthFile, True): print "Failed to authenticate Music Manager." raise PodcastSyncDestinationException("Authentication Failure.") else: try: self.musicManagerauthenticated = self.musicManager.login(oAuthFile) except gmusicapi.exceptions.AlreadyLoggedIn: pass username = raw_input("Enter Google Username: "******"Enter Google Password: "******"Authentication Failure.") # perform a push task. should return a PodFastPodcastPushedEpisode instance def pushEpisode(self, podcastSyncTaskEpisodePush): (uploaded, matched, not_uploaded) = self.musicManager.upload([podcastSyncTaskEpisodePush.localFilename]) songGoogleMusicID = "" if not_uploaded: # If the track was not uploaded, it may have been uploaded in the past. p = re.compile("ALREADY_EXISTS\\((.*)\\)") m = p.findall(not_uploaded[podcastSyncTaskEpisodePush.localFilename]) songGoogleMusicID = m[0] else: songGoogleMusicID = uploaded[podcastSyncTaskEpisodePush.localFilename] print "Track uploaded Successfully. ID:" + songGoogleMusicID gmusicPlayLists = self.mobileClient.get_all_playlists() playListExists = False gmusicPlaylistID = "" for gmusicPlayList in gmusicPlayLists: if gmusicPlayList["name"] == podcastSyncTaskEpisodePush.playlistName: playListExists = True gmusicPlaylistID = gmusicPlayList["id"] break if not playListExists: print "Creating playlist..." gmusicPlaylistID = self.mobileClient.create_playlist(podcastSyncTaskEpisodePush.playlistName) addedEntries = self.mobileClient.add_songs_to_playlist(gmusicPlaylistID, [songGoogleMusicID]) print "Moved track to playlist." return songGoogleMusicID # Pull (deletes) an episode from the destination returns true on success, False on faiilure def pullEpisode(self, podcastSyncTaskEpisodePull): self.mobileClient.delete_songs([podcastSyncTaskEpisodePull.syncDestinationID]) # TODO: Error check here. return True
class GoogleMusicProvider: api = None def __init__(self, login, password, android_id, *args): self.api = Mobileclient() auth = self.api.login(login, password, android_id) if not auth: self.api.get print('GPM login: {0}'.format(auth)) def search(self, query): return self.api.search(query, max_results=1)['song_hits'][0]['track'] def getUrl(self, id): return self.api.get_stream_url(id) def add_playlist(self, name): return self.api.create_playlist(name) def add_to_playlist(self, trackId, playlistId): self.api.add_songs_to_playlist(playlist_id=playlistId, song_ids=trackId)
class GmSession: def __init__(self): self.session = Mobileclient() self.device_id = gmusic_device_id self.cred_path = gmusic_cred_path self.playlist_id = gmusic_playlist_id def login(self): self.session.oauth_login(device_id=self.device_id, oauth_credentials=self.cred_path) def logout(self): self.session.logout() def search(self, artist, song): search_string = f'{artist.lower()}' + f', {song.lower()}' results = self.session.search(search_string, max_results=20) if len(results['song_hits']) > 0: first_result = results['song_hits'][0]['track'] if 'storeId' in first_result.keys(): return first_result['storeId'] elif 'id' in first_result.keys(): print('bad id') return first_result['id'] elif 'nid' in first_result.keys(): print('bad id') return results['song_hits'][0]['track']['nid'] else: print('No songs found...') def add_to_playlist(self, song_list): playlists = self.session.get_all_user_playlist_contents() for playlist in playlists: if playlist['id'] == self.playlist_id: to_remove = [] for track in playlist['tracks']: to_remove.append(track['id']) print('Adding new songs...') res = self.session.add_songs_to_playlist( self.playlist_id, song_list) print('Removing previous songs...') out = self.session.remove_entries_from_playlist(to_remove) print('Finished')
if playlist["name"] == playlistName: if args.replace: logger.info("Deleting existing playlist") gpm.delete_playlist(playlist["id"]) else: logger.error("Playlist already exists: " + playlistName) exit(1) logger.info("Creating playlist: " + playlistName) playlistId = gpm.create_playlist(playlistName, description="Autogenerated by RPG", public=False) logger.info("Adding " + str(len(trackRows)) + " tracks to playlist") trackIds = [] for row in trackRows: trackIds.append(row[0]) if len(trackIds) >= 50: gpm.add_songs_to_playlist(playlist_id=playlistId, song_ids=trackIds) trackIds = [] logger.debug("Added 50 tracks to playlist") time.sleep(10) if len(trackIds) > 0: gpm.add_songs_to_playlist(playlist_id=playlistId, song_ids=trackIds) logger.debug("Added remaining " + str(len(trackIds)) + " tracks to playlist") logger.info("DONE!") db.close()
class GoogleMusic: __metaclass__ = Singleton SKIP_ARTIST = ['vox freaks'] def __init__(self): self.gmusicapi = Mobileclient(debug_logging=False) logged_in = self.gmusicapi.login( email=Config.GOOGLE_MUSIC_USER_NAME, password=Config.GOOGLE_MUSIC_APP_PASSWORD, locale='en_US', android_id=Mobileclient.FROM_MAC_ADDRESS) if not logged_in: raise Exception('Unable to log in to GoogleMusic') def get_lib_from_gmusic(self): lib = self.gmusicapi.get_all_songs() lib_list = [] song = None for song in lib: song = Song(artist=song['artist'], title=song['title']) lib_list.append(song) return lib_list def delete_playlist_if_exists(self, name): all_playlists = self.gmusicapi.get_all_playlists() for playlist in all_playlists: if playlist['name'] == name: self.gmusicapi.delete_playlist(playlist['id']) def create_playlist(self, name, description, public=True): return self.gmusicapi.create_playlist(name=name, description=description, public=public) def add_songs_to_playlist(self, playlist_id, song_ids=None, song_df=None): if (song_ids is None and song_df is None): raise ValueError('Need song_ids or song_dfs to add to playlist') if song_df is not None and (not song_df.empty): song_ids = song_df.google_music_store_id.dropna().tolist() return self.gmusicapi.add_songs_to_playlist(playlist_id, song_ids) def gmusic_constrained_search(self, song, query, strict): song_hits = query['song_hits'] for result in song_hits: track = result['track'] if track['albumArtist'].lower() in SKIP_ARTIST: continue if song.remix: if "remix" not in track['title'].lower(): continue else: if "remix" in track['title'].lower(): continue if strict: full_title = "{} - {}".format(track['albumArtist'], track['title']) score = difflib.SequenceMatcher(None, song.full_title.lower(), full_title.lower()).ratio() if score < 0.6: continue return track return None def search_song(self, song, strict=False): try: first_query = self.gmusicapi.search(song.full_title) first_result = self.gmusic_constrained_search( song, first_query, strict) if first_result is None: second_query = self.gmusicapi.search(song.full_title_stripped) first_result = self.gmusic_constrained_search( song, second_query, strict) if first_result is None: logger.warning( 'No satisfactory result found in Google Music for {}'. format(song.full_title)) return first_result except Exception as e: logger.debug('Exception: {}'.format(e)) logger.info(u'Skipped {}'.format(song.title)) return None def get_store_id(self, result): store_id = None if result: if result.has_key('storeId'): store_id = result['storeId'] return store_id def get_google_rating(self, result): rating = None if result: if result.has_key('rating'): return result['rating'] return rating def update_playlist(self, playlist, public=True, exclude_0_rating=True): #Delete Playlist if present. logger.info(u'Updating the playlist {} in GoogleMusic'.format( playlist.name)) self.delete_playlist_if_exists(playlist.name) #Create Playlist playlist_id = self.create_playlist(name=playlist.name, description=playlist.description, public=public) if exclude_0_rating: playlist.song_df = playlist.song_df[ playlist.song_df['google_music_rating'] != '1'] self.add_songs_to_playlist(playlist_id=playlist_id, song_df=playlist.song_df)
log.warning('Skipping playlist ' + playlist_name + ' (no tracks available in All Access)') continue log.debug('Creating playlist ' + playlist_name) if not simulate: try: playlist_id = import_api.create_playlist(playlist_name) except CallFailure as e: log.error('Failed to create playlist ' + playlist_name) continue log.debug('Adding ' + str(len(track_ids)) + ' tracks to playlist') if not simulate: try: import_api.add_songs_to_playlist(playlist_id, track_ids) except CallFailure as e: log.error('Failed to add tracks to playlist ' + playlist_name) continue # import all radio stations if migration_type == 'all' or migration_type == 'stations': log.info('Importing ' + str(len(radio_stations)) + ' radio stations to ' + import_username) for station in radio_stations: station_name = station.get('name') station_id = get_station_id(station) if not station_id: log.warning(
class FetchSongs(object): """ Class to manage updating and creating of playlists and songs in the Google Play Music service for a specific Play Music account """ def __init__(self): self.songs = [] self.nids = [] os.environ['REQUESTS_CA_BUNDLE'] = '/etc/ssl/certs/ca-certificates.crt' self.api = Mobileclient() self.api.login('xxxxxx', 'xxxxx', Mobileclient.FROM_MAC_ADDRESS) def add_songs_to_gmusic_playlist(self, playlist_id, song_ids): """ Add songs to the GMUSIC API playlist using song ids :param playlist_id: str playlist id :param song_ids: list of strings :returns: None """ return self.api.add_songs_to_playlist(playlist_id, song_ids) def search_for_songs(self, artist, title): """ Search for songs in the GMUSIC API :param artist: string :param title: string :return song_nid: string """ song_nid = None search_content = self.api.search(''.join([artist, ' ', title])) for each_song in search_content['song_hits']: song_detail = box.Box(each_song['track']) if artist.casefold() in song_detail.artist.casefold( ) and title.casefold() in song_detail.title.casefold(): song_nid = song_detail.storeId break return song_nid def get_playlists_length(self, api_content): """ Get size of GMUSIC playlists in terms of number of songs :param api_content: dict :returns playlist_sizes: dict """ playlist_sizes = {} api_playlists = self.api.get_all_playlists() for api_playlist in api_playlists: for content_playlist in api_content: if content_playlist['id'] == api_playlist['id']: playlist_sizes[api_playlist['id']] = len( content_playlist['tracks']) return playlist_sizes def get_available_playlists(playlist_dict): """ Get available playlist ids and associated number of songs :param playlist_dict: dict :return list_id, number_of_songs: tuple of list_id and number of songs """ available_lists = {k for (k, v) in playlist_dict.items() if v <= 800} if available_lists: for list_id, number_of_songs in available_lists.items(): yield (list_id, number_of_songs) else: return None @classmethod def create_gmusic_playlist(self, name): """ Create GMUSIC playlist :param name: string name of playlist :return: string success or fail""" return self.api.create_playlist(name)
def allaccessimport(playlist=None, username=None, password=None, dry_run=False): """ Exports a Spotify playlist to stdout or csv. """ if not username or not password: raise CommandError( "Username and password must be provided as either command-line " + "argument or in the application configuration file." ) playlist_name = playlist playlist_description = "" if playlist: playlist_name = os.path.basename(playlist_name) playlist_name = os.path.splitext(playlist_name)[0] logging.debug("Playlist name will be: {}".format(playlist_name)) api = Mobileclient(False, False) logged_in = api.login(username, password, Mobileclient.FROM_MAC_ADDRESS) if not logged_in: raise CommandError('Error. Unable to login to Google Music All Access.') playlist_ref = None currenttracks = None failed_tracks = list() songs_added = 0 total = 0 stream = open(playlist, "rb") if playlist else sys.stdin for input_line in stream: input_line = input_line.strip() # Lazily search the beginning of the file for a Playlist name if input_line.startswith("#"): data = input_line[1:] parts = [x.strip() for x in data.split(":", 1)] if len(parts) == 2: if parts[0] == "Playlist": playlist_name = parts[1] elif parts[0] == "Description": playlist_description = parts[1] continue if not playlist_ref: if not playlist_name: raise CommandError( "A playlist name was not given and it was not found " + "in the file either. Can't continue." ) else: playlist_ref, currenttracks = get_playlist(api, playlist_name) if not playlist_ref and not dry_run: sys.stderr.write('Playlist not found. Creating new.\n') playlist_ref = api.create_playlist(playlist_name, description=playlist_description) yield 'Going to update playlist {0} ({1})\n'.format( playlist_name, playlist_ref ) trackinfo = list(csv.reader([input_line], quoting=csv.QUOTE_ALL))[0] if trackinfo[0] == 'Track' and trackinfo[1] == 'Artist': yield 'Skipping header.' continue search_term = "{0} {1}".format(trackinfo[0], trackinfo[1]) total = total + 1 newtrackid, error_reason = search_track(api, search_term, currenttracks) if newtrackid: if not dry_run: #print("Add to {} song {}".format(playlist_ref, newtrackid)) api.add_songs_to_playlist(playlist_ref, newtrackid) songs_added = songs_added + 1 else: failed_tracks.append(trackinfo) sys.stderr.write( "Searching {}...{}\n".format(search_term, error_reason) ) yield "{0} songs added out of {1}. {2} Failed.".format( songs_added, total, total-songs_added ) yield "Failed tracks:" for line in failed_tracks: print " ", line
song = {} tids = [] for i,v in enumerate(playlist): tids.append(v['trackId']) for i,v in enumerate(songs): if v['id'] not in tids: song = v errSong = None try: guid = song['id'] except: #library = api.get_all_songs() errSong = clusters[cluster][random.randint(0, len(clusters[cluster])-1)] guid = errSong['id'] #device = webapi.get_registered_devices() #print device[0]['id'][2:] #streamURL = api.get_stream_url(guid, device[0]['id'][2:]) streamURL = api.get_stream_url(guid, '320b3128904ea650') if errSong is None: #pid = api.create_playlist("Tempo: " + dt.datetime.now().strftime("%m-%d-%Y %I:%M:%S%p")) api.add_songs_to_playlist(pid, guid) print json.dumps({"streamURL": streamURL, "song": song}, indent=4, separators=(',', ': ')) else: api.add_songs_to_playlist(pid, guid) print json.dumps({"streamURL": streamURL, "song": errSong, "error": "true"}, indent=4, separators=(',', ': '))
class GMusicClient(ContentConsumer): '''Element in charge of interfacing with GMusicApi Client''' def __init__(self, data_cache): self.client = Mobileclient() self.data_cache = data_cache def login(self): '''Use data/unlocked/credentials.json to log in''' mac = Mobileclient.FROM_MAC_ADDRESS credentials = json.load(open('data/unlocked/credentials.json', 'r')) self.client.login(credentials['username'], credentials['password'], mac) def load_my_library(self): '''Load user's songs, playlists, and stations''' track_load = threading.Thread(target=self.load_tracks) radio_load = threading.Thread(target=self.load_radios) playlist_load = threading.Thread(target=self.load_playlists) track_load.start() radio_load.start() playlist_load.start() track_load.join() radio_load.join() playlist_load.join() def load_playlists(self): playlists = self.client.get_all_user_playlist_contents() playlists.reverse() self.data_cache.playlists = playlists def load_tracks(self): self.data_cache.tracks = [t for t in self.client.get_all_songs() if 'nid' in t] def scrape_song(self, track): return track def load_radios(self): radios = self.client.get_all_stations() radios.reverse() self.data_cache.radios = radios def get_radio_contents(self, radio_id): tracks = self.client.get_station_tracks(radio_id) return tracks def get_radio_list(self, name): return [r for r in self.data_cache.radios if name in r['name']] def filter(self, element, field, filter_by): return [e for e in element if filter_by in e[field]] def get_playlist_list(self, name): return self.filter(self.data_cache.playlists, 'name', name) def search_all_access(self, query): return self.client.search_all_access(query) def create_radio(self, seed_type, id, name): '''Create a radio''' # This is a weird way to do this, but there's no other choice ids = {"track": None, "album": None, "artist": None} seed_id_name = self.get_type_name(seed_type) ids[seed_id_name] = id return self.client.create_station(name=name, track_id=ids['track'], album_id=ids['album'], artist_id=ids['artist']) def search_items_all_access(self, type, query): '''Searches Albums, Artists, and Songs; uses metaprogramming''' index_arguments = self.get_index_arguments(type) items = self.search_all_access(query)['{0}_hits'.format(type[:-1])] return [self.format_item(item, type, index_arguments) for item in items] def get_sub_items(self, type_from, search_type, from_id): '''Here type_from refers to artist or album we're indexing against''' args = self.get_index_arguments(search_type) if type_from == 'playlist': return self.get_playlist_contents(from_id) else: # Get the appropriate search method and execute it search_method_name = 'get_{0}_info'.format(type_from) search_method = getattr(self.client, search_method_name) try: items = search_method(from_id, True)[args['type']+'s'] # True here includes subelements except: # User passed in a bad id or something return # Now return appropriately return [self.format_subitems(t, args) for t in items if args['id'] in t] def get_playlist_contents(self, from_id): '''Playlist exclusive stuff''' shareToken = [t for t in self.data_cache.playlists \ if t['id'] == from_id][0]['shareToken'] contents = self.client.get_shared_playlist_contents(shareToken) return [self.format_playlist_contents(t) for t in contents if 'track' in t] def get_suggested(self): '''Returns a list of tracks that the user might be interested in''' items = sorted(self.client.get_promoted_songs(), key=lambda y: y['title']) return [self.format_suggested(t) for t in items if 'storeId' in t] def get_information_about(self, from_type, from_id): '''Gets specific information about an id (depending on the type)''' if 'artist' in from_type.lower(): return self.client.get_artist_info(from_id, include_albums=False) if 'album' in from_type.lower(): return self.client.get_album_info(from_id, include_tracks=False) return self.client.get_track_info(from_id) def get_stream_url(self, nid): return self.client.get_stream_url(nid) def lookup(self, nid): return self.client.get_track_info(nid) def add_track_to_library(self, nid): self.client.add_aa_track(nid) def add_to_playlist(self, playlist_id, nid): self.client.add_songs_to_playlist(playlist_id, nid) playlist_load = threading.Thread(target=self.load_playlists) playlist_load.daemon = True playlist_load.start() def format_suggested(self, t): return (t['title'], t['storeId'], 'Play', t['album']) def format_playlist_contents(self, t): return (t['track']['title'], t['trackId'], 'Play', t['track']['album']) def format_subitems(self, t, args): return (t[args['name']], t[args['id']], args['command'], t[args['alt']])
class MusicSync(object): def __init__(self, email=None, password=None): self.mm = Musicmanager() self.wc = Webclient() self.mc = Mobileclient() if not email: email = raw_input("Email: ") if not password: password = getpass() self.email = email self.password = password self.logged_in = self.auth() print "Fetching playlists from Google..." self.playlists = self.mc.get_all_user_playlist_contents() #self.playlists = self.mc.get_all_playlists() #self.playlists = self.wc.get_all_playlist_ids(auto=False) self.all_songs = self.mc.get_all_songs() #print "Got %d playlists." % len(self.playlists['user']) print "Got %d playlists containing %d songs." % (len(self.playlists), len(self.all_songs)) print "" def auth(self): self.logged_in = self.mc.login(self.email, self.password) #self.logged_in = self.wc.login(self.email, self.password) if not self.logged_in: print "Login failed..." exit() print "" print "Logged in as %s" % self.email print "" if not os.path.isfile(OAUTH_FILEPATH): print "First time login. Please follow the instructions below:" self.mm.perform_oauth() self.logged_in = self.mm.login() if not self.logged_in: print "OAuth failed... try deleting your %s file and trying again." % OAUTH_FILEPATH exit() print "Authenticated" print "" def sync_playlist(self, filename, remove_missing): #def sync_playlist(self, filename, remove_missing=False): filename = self.get_platform_path(filename) os.chdir(os.path.dirname(filename)) title = os.path.splitext(os.path.basename(filename))[0] print "Syncing playlist: %s" % filename #if title not in self.playlists['user']: #print " didn't exist... creating..." #self.playlists['user'][title] = [self.wc.create_playlist(title)] print "" plid = "" for pl in self.playlists: if pl['name'] == title: plid = pl['id'] goog_songs = pl['tracks'] if plid == "": print " didn't exist... creating..." plid = self.mc.create_playlist(self, title) #plid = self.playlists['user'][title][0] #goog_songs = self.wc.get_playlist_songs(plid) print "%d songs already in Google Music playlist" % len(goog_songs) pc_songs = self.get_files_from_playlist(filename) print "%d songs in local playlist" % len(pc_songs) print "" # Sanity check max 1000 songs per playlist if len(pc_songs) > MAX_SONGS_IN_PLAYLIST: print " Google music doesn't allow more than %d songs in a playlist..." % MAX_SONGS_IN_PLAYLIST print " Will only attempt to sync the first %d songs." % MAX_SONGS_IN_PLAYLIST del pc_songs[MAX_SONGS_IN_PLAYLIST:] existing_files = 0 added_files = 0 failed_files = 0 removed_files = 0 fatal_count = 0 for fn in pc_songs: if self.file_already_in_list(fn, goog_songs, self.all_songs): existing_files += 1 continue print "" print "Adding: %s" % os.path.basename(fn).encode('cp1252') #print "Adding: %s" % os.path.basename(fn) #online = False online = self.find_song(fn, goog_songs, self.all_songs) #online = self.find_song(fn) song_id = None if online: song_id = online['id'] print " already uploaded [%s]" % song_id else: attempts = 0 result = [] while not result and attempts < MAX_UPLOAD_ATTEMPTS_PER_FILE: print " uploading... (may take a while)" attempts += 1 try: result = self.mm.upload(fn) except (BadStatusLine, CannotSendRequest): # Bail out if we're getting too many disconnects if fatal_count >= MAX_CONNECTION_ERRORS_BEFORE_QUIT: print "" print "Too many disconnections - quitting. Please try running the script again." print "" exit() print "Connection Error -- Reattempting login" fatal_count += 1 self.wc.logout() self.mc.logout() self.mm.logout() result = [] time.sleep(STANDARD_SLEEP) except: result = [] time.sleep(STANDARD_SLEEP) try: if result[0]: song_id = result[0].itervalues().next() else: song_id = result[1].itervalues().next() print " upload complete [%s]" % song_id except: print " upload failed - skipping" tag = self.get_id3_tag(fn) print " failed song:\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252')) if not song_id: failed_files += 1 continue added = self.mc.add_songs_to_playlist(plid, song_id) time.sleep(.3) # Don't spam the server too fast... print " done adding to playlist" added_files += 1 if remove_missing: for g in goog_songs: for s in self.all_songs: if g['trackId'] == s['id']: print "" print "Removing: %s" % s['title'].encode('cp1252') self.mc.remove_entries_from_playlist(g['id']) #self.wc.remove_songs_from_playlist(plid, s.id) time.sleep(.3) # Don't spam the server too fast... removed_files += 1 print "" print "---" print "%d songs unmodified" % existing_files print "%d songs added" % added_files print "%d songs failed" % failed_files print "%d songs removed" % removed_files def get_files_from_playlist(self, filename): files = [] f = codecs.open(filename, encoding='cp1252') #f = codecs.open(filename, encoding='utf-8') for line in f: line = line.rstrip().replace(u'\ufeff',u'') if line == "" or line[0] == "#": continue path = os.path.abspath(self.get_platform_path(line)) if not os.path.exists(path): print "File not found: %s" % line continue files.append(path) f.close() return files def file_already_in_list(self, filename, goog_songs, all_songs): tag = self.get_id3_tag(filename) print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252')) i = 0 while i < len(goog_songs): for s in all_songs: if goog_songs[i]['trackId'] == s['id']: if self.tag_compare(s, tag): print "Found match\t%s\t%s\t%s" % (s['title'].encode('cp1252'), s['artist'].encode('cp1252'), s['album'].encode('cp1252')) goog_songs.pop(i) return True i += 1 return False def get_id3_tag(self, filename): data = mutagen.File(filename, easy=True) r = {} if 'title' not in data: title = os.path.splitext(os.path.basename(filename))[0] print 'Found song with no ID3 title, setting using filename:' print ' %s' % title print ' (please note - the id3 format used (v2.4) is invisible to windows)' data['title'] = [title] data.save() r['title'] = data['title'][0] r['track'] = int(data['tracknumber'][0].split('/')[0]) if 'tracknumber' in data else 0 # If there is no track, try and get a track number off the front of the file... since thats # what google seems to do... # Not sure how google expects it to be formatted, for now this is a best guess if r['track'] == 0: m = re.match("(\d+) ", os.path.basename(filename)) if m: r['track'] = int(m.group(0)) r['artist'] = data['artist'][0] if 'artist' in data else '' r['album'] = data['album'][0] if 'album' in data else '' return r def find_song(self, filename, goog_songs, all_songs): tag = self.get_id3_tag(filename) print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252')) #results = self.wc.search(tag['title']) # NOTE - diagnostic print here to check results if you're creating duplicates #print results['song_hits'] #for r in goog_songs: #for r in results['song_hits']: for s in all_songs: #if r['trackId'] == s['id']: if self.tag_compare(s, tag): # TODO: add rough time check to make sure its "close" print "Found match\t%s\t%s\t%s" % (s['title'].encode('cp1252'), s['artist'].encode('cp1252'), s['album'].encode('cp1252')) return s return None def tag_compare(self, g_song, tag): # If a google result has no track, google doesn't return a field for it if 'title' not in g_song: g_song['title'] = "" if 'artist' not in g_song: g_song['artist'] = "" if 'album' not in g_song: g_song['album'] = "" if 'track' not in g_song: g_song['track'] = 0 if (g_song['title'].lower() == tag['title'].lower() and g_song['artist'].lower() == tag['artist'].lower()) or\ (g_song['album'].lower() == tag['album'].lower() and g_song['title'].lower() == tag['title'].lower()) or\ (g_song['artist'].lower() == tag['artist'].lower() and g_song['album'].lower() == tag['album'].lower() and g_song['track'] == tag['track']): print "Partial match\t%s\t%s\t%s" % (g_song['title'].encode('cp1252'), g_song['artist'].encode('cp1252'), g_song['album'].encode('cp1252')) return g_song['title'].lower() == tag['title'].lower() and\ g_song['artist'].lower() == tag['artist'].lower() and\ g_song['album'].lower() == tag['album'].lower() #and\ #g_song['track'] == tag['track'] def delete_song(self, sid): self.mc.delete_songs(sid) print "Deleted song by id [%s]" % sid def get_platform_path(self, full_path): # Try to avoid messing with the path if possible if os.sep == '/' and '\\' not in full_path: return full_path if os.sep == '\\' and '\\' in full_path: return full_path if '\\' not in full_path: return full_path return os.path.normpath(full_path.replace('\\', '/'))
if token: sp = spotipy.Spotify(auth=token) playlists = sp.user_playlists(username) for playlist in playlists['items']: if playlist['owner']['id'] == username and playlist['name'] not in current_playlists: print playlist_name = playlist['name'] print "Creating playlist: " + playlist_name # Create new Google Play Music playlist with the same name as the Spotify playlist playlist_id = mc.create_playlist(playlist_name) # Extract the tracks from the Spotify playlist results = sp.user_playlist(username, playlist['id'], fields="tracks,next") tracks = results['tracks'] # Obtain the Google Play Music track IDs and add them to the new playlist track_ids = find_tracks(tracks, mc) mc.add_songs_to_playlist(playlist_id, track_ids) # Loop for playlists that are longer than 100 tracks while tracks['next']: tracks = sp.next(tracks) track_ids = find_tracks(tracks, mc) print track_ids mc.add_songs_to_playlist(playlist_id, track_ids) else: print "Cannot get valid token for Spotify" else: print "Cannot login to Google Play Music"
print "usage:" + sys.argv[0] + " filename [playlist name]" sys.exit() file = params[1] if len(params) == 3: plname = params[2] else: plname = None mm = Musicmanager() api = Mobileclient() mm.login() api.login('GoogleID', 'Password') track = mm.upload(file) track_id = track[0][file] if plname: playlist_id = None playlists = api.get_all_playlists() for playlist in playlists: if plname == playlist['name']: playlist_id = playlist['id'] break if playlist_id == None: playlist_id = api.create_playlist(plname) api.add_songs_to_playlist(playlist_id, track_id)
class PlaylistManager(object): def __init__(self, email_file): self.login(email_file) self._get_songs() self._get_playlists() def login(self, email_file): with open(os.path.expanduser(email_file), 'r') as gmail_email_file: gmail_email = gmail_email_file.read() print "Password for " + gmail_email.strip('\n') + ":" pw = getpass.getpass() self.client = Mobileclient() self.client.login(gmail_email, pw) def _get_songs(self): self.all_songs = self.client.get_all_songs() assert isinstance(self.all_songs, list) self.all_songs_by_id = {} for song in self.all_songs: self.all_songs_by_id[song['id']] = song def _playlist_entry_dict(self, entry_dict, playlist_name): track_dict = self.all_songs_by_id[entry_dict['trackId']] track_hash = hash(playlist_name + track_dict['title'] + track_dict['artist'] + track_dict['album']) return {'title': track_dict['title'], 'entry_id': entry_dict['id'], 'track_id': entry_dict['trackId'], 'hash': track_hash} def _get_playlists(self): self.all_playlists = self.client.get_all_user_playlist_contents() assert isinstance(self.all_playlists, list) def sort_and_deduplicate_playlists(self): """de-duplicates and then sorts all user playlists""" playlist_entry_ids_to_be_removed = [] for playlist in self.all_playlists: current_playlist = [] current_playlist_entry_hashes = [] for track in playlist['tracks']: if not track['deleted']: current_track = self._playlist_entry_dict(track, playlist['name']) if not current_track['hash'] in current_playlist_entry_hashes: current_playlist.append(current_track) current_playlist_entry_hashes.append(current_track['hash']) else: # we will remove these duplicates later, so they don't mess up our sorting playlist_entry_ids_to_be_removed.append(current_track['entry_id']) if playlist_entry_ids_to_be_removed != []: print "De-duplicated " + playlist['name'] get_track_title = lambda track_dict: track_dict['title'] sorted_songs = sorted(current_playlist, key=get_track_title) sorted_track_ids = [] sorted_entry_ids = [] for track in sorted_songs: sorted_track_ids.append(track['track_id']) sorted_entry_ids.append(track['entry_id']) # if the playlist is already sorted, don't bother sorting it if sorted_songs != current_playlist: # remove all things at once for maximum efficiency self.client.add_songs_to_playlist(playlist['id'], sorted_track_ids) playlist_entry_ids_to_be_removed += sorted_entry_ids print "Re-ordered " + playlist['name'] if playlist_entry_ids_to_be_removed != []: # remove all things to be removed at once self.client.remove_entries_from_playlist(playlist_entry_ids_to_be_removed) def export_songs(self, csv_output): """writes a csv file containing info on all the songs""" # these are the field names. More might exist, but I haven't found them field_names = [u'comment', u'rating', u'composer', u'year', u'creationTimestamp', u'id', u'album', u'totalDiscCount', u'title', u'recentTimestamp', u'albumArtist', u'trackNumber', u'discNumber', u'deleted', u'totalTrackCount', u'estimatedSize', u'beatsPerMinute', u'genre', u'playCount', u'kind', u'artist', u'lastModifiedTimestamp', u'clientId', u'durationMillis', u'albumArtRef', u'artistId', u'storeId', u'nid', u'albumId', u'artistArtRef', u'trackType', u'trackOrigin', u'contentType', u'lastRatingChangeTimestamp'] with open(csv_output, 'w') as output_file_obj: csv_writer = csv.DictWriter(output_file_obj, field_names) csv_writer.writeheader() for song in self.all_songs: for x in song: # remove the unicode, replacing bad characters with "?" if isinstance(song[x], unicode): song[x] = song[x].encode('ascii', 'replace') csv_writer.writerow(song) def export_playlists(self, output_path): """writes the csv contents of each playlist to the playlist-named file in the specified folder""" if not os.path.exists(output_path): os.mkdir(output_path) if not os.path.isdir(output_path): raise Exception("Not a folder!") playlist_entry_keys = [u'absolutePosition', u'clientId', u'creationTimestamp', u'deleted', u'id', u'kind', u'lastModifiedTimestamp', u'playlistId', u'source', u'trackId'] for playlist in self.all_playlists: file_name = os.path.join(output_path, playlist[u'name'] + '.csv') with open(file_name, 'w') as output_file: csv_writer = csv.DictWriter(output_file, playlist_entry_keys) csv_writer.writeheader() for track in playlist[u'tracks']: for x in track: # remove the unicode, replacing bad characters with "?" if isinstance(track[x], unicode): track[x] = track[x].encode('ascii', 'replace') csv_writer.writerow(track) def make_local_xspf_playlists(self): """write playlists in xspf format to named files in the playlist folder. Credit to https://github.com/alastair/xspf for the wonderful xspf python module""" # output_path = 'playlists' output_path = os.path.expanduser('~/Music/Playlists') music_path = os.path.expanduser('~/Music') extension = '.mp3' if not os.path.exists(output_path): os.mkdir(output_path) if not os.path.isdir(output_path): raise Exception("Not a folder!") for playlist in self.all_playlists: output_file_path = os.path.join(output_path, playlist[u'name'] + '.xspf') playlist_xspf = xspf.Xspf() playlist_xspf.title = playlist[u'name'] for track in playlist['tracks']: track_xspf = xspf.Track() track_id = track['trackId'] track_dict = self.all_songs_by_id[track_id] artist = track_dict['artist'] album = track_dict['album'] title = track_dict['title'] track_xspf.title = title track_xspf.album = album track_xspf.creator = artist # replace is to fix the slash in 'AC/DC' track_path = 'file://' + os.path.join(music_path, artist.replace('/', ','), album.replace('/', ','), title.replace('/', ',') + extension) track_xspf.location = track_path length = str(int(track_dict['durationMillis']) / 1000) track_xspf.duration = length playlist_xspf.add_track(track_xspf) with open(output_file_path, 'wb') as output_file: # we write this header so that Sublime Text recognizes it as an XML file, and sets syntax accordingly output_file.write('<?xml version="1.0" encoding="UTF-8"?>\n') output_file.write(playlist_xspf.toXml(pretty_print=True))
count = min(len(table), args.li) else: count = len(table) if logged_in: playLists = api.get_all_user_playlist_contents() playlist = next(p for p in playLists if p['name'] == args.pl) for i in range(0, count): column = table[i].xpath('td') """SomaFM prints breaks and in the schedule so we only want rows in the table that are a track""" if len(column) == 5: song.artist = column[1].xpath('a')[0].text song.songName = column[2].text song.album = column[3].xpath('a')[0].text result = api.search_all_access(song.artist + " " + song.songName, 1) if result['song_hits']: songInPlaylist = False for track in playlist['tracks']: if track['trackId'] == result['song_hits'][0]['track']['nid']: print 'playlist already contains ', song.songName songInPlaylist = True if not songInPlaylist: print 'playlist dose not already contain ', song.songName api.add_songs_to_playlist(playlist['id'], result['song_hits'][0]['track']['nid'])
if len(track_ids) == 0: log.warning('Skipping playlist ' + playlist_name + ' (no tracks available in All Access)') continue log.debug('Creating playlist ' + playlist_name) if not simulate: try: playlist_id = import_api.create_playlist(playlist_name) except CallFailure as e: log.error('Failed to create playlist ' + playlist_name) continue log.debug('Adding ' + str(len(track_ids)) + ' tracks to playlist') if not simulate: try: import_api.add_songs_to_playlist(playlist_id, track_ids) except CallFailure as e: log.error('Failed to add tracks to playlist ' + playlist_name) continue # import all radio stations if migration_type == 'all' or migration_type == 'stations': log.info('Importing ' + str(len(radio_stations)) + ' radio stations to ' + import_username) for station in radio_stations: station_name = station.get('name') station_id = get_station_id(station) if not station_id: log.warning('Skipping station with invalid ID (probably not All Access)') continue
class GMusic(object): def __init__(self): self.authenticated = False self.all_access = False self.library_loaded = False self.all_songs = [] self.letters = {} self.artists = {} self.albums = {} self.genres = {} self.tracks_by_letter = {} self.tracks_by_artist = {} self.tracks_by_album = {} self.tracks_by_genre = {} self._device = None self._webclient = Webclient(debug_logging=False) self._mobileclient = Mobileclient(debug_logging=False) self._playlists = [] self._playlist_contents = [] self._stations = [] def _get_device_id(self): if self.authenticated: devices = self._webclient.get_registered_devices() for dev in devices: if dev['type'] == 'PHONE': self._device = dev['id'][2:] break elif dev['type'] == 'IOS': self._device = dev['id'] break def _set_all_access(self): settings = self._webclient._make_call(webclient.GetSettings, '') self.all_access = True if 'isSubscription' in settings['settings'] and settings['settings']['isSubscription'] == True else False def _set_all_songs(self): if len(self.all_songs) == 0: try: self.all_songs = self._mobileclient.get_all_songs() except NotLoggedIn: if self.authenticate(): self.all_songs = self._mobileclient.get_all_songs() else: return [] else: return self.all_songs def authenticate(self, email, password): try: mcauthenticated = self._mobileclient.login(email, password) except AlreadyLoggedIn: mcauthenticated = True try: wcauthenticated = self._webclient.login(email, password) except AlreadyLoggedIn: wcauthenticated = True self.authenticated = mcauthenticated and wcauthenticated self._set_all_access() self._get_device_id() return self.authenticated def load_data(self): self._set_all_songs() for song in self.all_songs: thumb = None letter = song['title'][0] artist = song['artist'] album = song['album'] genre = song['genre'] if 'genre' in song else '(None)' if letter not in self.tracks_by_letter: self.tracks_by_letter[letter] = [] self.letters[letter] = None if artist not in self.tracks_by_artist: self.tracks_by_artist[artist] = [] self.artists[artist] = None if album not in self.tracks_by_album: self.tracks_by_album[album] = [] self.albums[album] = None if genre not in self.tracks_by_genre: self.tracks_by_genre[genre] = [] self.genres[genre] = None track = {'artist': artist, 'album': album} if 'title' in song: track['title'] = song['title'] if 'album' in song: track['album'] = song['album'] if 'artist' in song: track['artist'] = song['artist'] if 'durationMillis' in song: track['durationMillis'] = song['durationMillis'] if 'id' in song: track['id'] = song['id'] if 'trackNumber' in song: track['trackType'] = song['trackNumber'] if 'storeId' in song: track['storeId'] = song['storeId'] if 'albumArtRef' in song: track['albumArtRef'] = song['albumArtRef'] thumb = song['albumArtRef'][0]['url'] self.letters[letter] = thumb self.artists[artist] = thumb self.albums[album] = thumb self.genres[genre] = thumb self.tracks_by_letter[letter].append({'track': track, 'thumb': thumb, 'id': song['id']}) self.tracks_by_artist[artist].append({'track': track, 'thumb': thumb, 'id': song['id']}) self.tracks_by_album[album].append({'track': track, 'thumb': thumb, 'id': song['id']}) self.tracks_by_genre[genre].append({'track': track, 'thumb': thumb, 'id': song['id']}) self.library_loaded = True def get_tracks_for_type(self, type, name): type = type.lower() if type == 'artists': return self.tracks_by_artist[name] elif type == 'albums': return self.tracks_by_album[name] elif type == 'genres': return self.tracks_by_genre[name] elif type == 'songs by letter': return self.tracks_by_letter[name] else: return {} def get_song(self, id): return [x for x in self.all_songs if x['id'] == id][0] def get_all_playlists(self): if len(self._playlists) == 0: try: self._playlists = self._mobileclient.get_all_playlists() except NotLoggedIn: if self.authenticate(): self._playlists = self._mobileclient.get_all_playlists() else: return [] return self._playlists def get_all_user_playlist_contents(self, id): tracks = [] if len(self._playlist_contents) == 0: try: self._playlist_contents = self._mobileclient.get_all_user_playlist_contents() except NotLoggedIn: if self.authenticate(): self._playlist_contents = self._mobileclient.get_all_user_playlist_contents() else: return [] for playlist in self._playlist_contents: if id == playlist['id']: tracks = playlist['tracks'] break return tracks def get_shared_playlist_contents(self, token): playlist = [] try: playlist = self._mobileclient.get_shared_playlist_contents(token) except NotLoggedIn: if self.authenticate(): playlist = self._mobileclient.get_shared_playlist_contents(token) else: return [] return playlist def get_all_stations(self): if len(self._stations) == 0: try: self._stations = self._mobileclient.get_all_stations() except NotLoggedIn: if self.authenticate(): self._stations = self._mobileclient.get_all_stations() else: return [] return self._stations def get_station_tracks(self, id, num_tracks=200): tracks = [] try: tracks = self._mobileclient.get_station_tracks(id, num_tracks) except NotLoggedIn: if self.authenticate(): tracks = self._mobileclient.get_station_tracks(id, num_tracks) else: return [] return tracks def get_genres(self): genres = [] try: genres = self._mobileclient.get_genres() except NotLoggedIn: if self.authenticate(): genres = self._mobileclient.get_genres() else: return [] return genres def create_station(self, name, id): station = None try: station = self._mobileclient.create_station(name=name, genre_id=id) except NotLoggedIn: if self.authenticate(): station = self._mobileclient.create_station(name=name, genre_id=id) else: return [] return station def search_all_access(self, query, max_results=50): results = None try: results = self._mobileclient.search_all_access(query, max_results) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.search_all_access(query, max_results) else: return [] return results def get_artist_info(self, id, include_albums=True, max_top_tracks=5, max_rel_artist=5): results = None try: results = self._mobileclient.get_artist_info(id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.get_artist_info(id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist) else: return [] return results def get_album_info(self, id, include_tracks=True): results = None try: results = self._mobileclient.get_album_info(id, include_tracks=include_tracks) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.get_album_info(id, include_tracks=include_tracks) else: return [] return results def add_aa_track(self, id): track = None try: track = self._mobileclient.add_aa_track(id) except NotLoggedIn: if self.authenticate(): track = self._mobileclient.add_aa_track(id) else: return None return track def add_songs_to_playlist(self, playlist_id, song_ids): tracks = None try: tracks = self._mobileclient.add_songs_to_playlist(playlist_id, song_ids) except NotLoggedIn: if self.authenticate(): tracks = self._mobileclient.add_songs_to_playlist(playlist_id, song_ids) else: return None return tracks def get_stream_url(self, id): try: stream_url = self._mobileclient.get_stream_url(id, self._device) except NotLoggedIn: if self.authenticate(): stream_url = self._mobileclient.get_stream_url(id, self._device) else: return '' except CallFailure: raise CallFailure('Could not play song with id: ' + id, 'get_stream_url') return stream_url def reset_library(self): self.library_loaded = False self.all_songs[:] = [] self._playlists[:] = [] self._playlist_contents[:] = [] self._stations[:] = [] self.letters.clear() self.artists.clear() self.albums.clear() self.genres.clear() self.tracks_by_letter.clear() self.tracks_by_artist.clear() self.tracks_by_album.clear() self.tracks_by_genre.clear()
print "Available genres:" for g in genres_map.keys(): if len(g) == 0: continue # empty genre tag if len(genres_map[g]) >= 20: print g + "\t" + str(len(genres_map[g])) + " tracks --> generating playlist" good_genres.append(g) else: print g + "\t" + str(len(genres_map[g])) + " tracks --> too few tracks" print # remove all existing auto-playlists first playlists = api.get_all_playlists() for p in playlists: if p["name"].startswith("genre auto-playlist"): print "Removing playlist " + p["name"] api.delete_playlist(p["id"]) for genre in good_genres: playlist = genres_map[genre] playlist_name = "genre auto-playlist - " + genre print "Creating playlist " + playlist_name pid = api.create_playlist( playlist_name, description="Test " + genre + " playlist created automatically with a script", public=False ) api.add_songs_to_playlist(pid, playlist)
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('-l', '--list', help='List playlists on the src account', action='store_true', dest='lst') parser.add_argument('-p', '--playlist', help='Playlist ID from src account to transfer', dest='playlist') args = parser.parse_args() api = Mobileclient() api.login(src_user, src_pass, src_device) playlists = api.get_all_playlists() if args.lst: for playlist in playlists: print playlist['name'] + ' (' + playlist['id'] + ') ' exit() library = api.get_all_songs() api2 = Mobileclient() api2.login(dst_user, dst_pass, dst_device) library2 = api2.get_all_songs() if args.playlist is None: print 'Error: no playlist selected' all_playlist_entries = api.get_all_user_playlist_contents() selected_playlist_entries = [] dst_playlist_id = None for entry in all_playlist_entries: if entry['id'] == args.playlist: selected_playlist_entries = entry['tracks'] dst_playlist_id = api2.create_playlist(entry['name']) if dst_playlist_id is None: print 'Error creating new playlist' exit() playlist_tracks = [] for ptrack in selected_playlist_entries: track_found = False for track in library: if ptrack['trackId'] == track['id']: playlist_tracks.append(track) track_found = True break try: if ptrack['trackId'] == track['storeId']: playlist_tracks.append(track) track_found = True break except: pass if not track_found: print 'ERROR: could not find playlist entry ' + str(ptrack) api.add_aa_track(ptrack['trackId']) if len(playlist_tracks) != len(selected_playlist_entries): print 'Error: could not locate all playlist tracks in src library' exit() failed_tracks = [] for track in playlist_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 store_track = api2.get_track_info(track['storeId']) #If we got here, we're good to go for adding the track to the playlist retval = api2.add_songs_to_playlist(dst_playlist_id, track['storeId']) if track['storeId'] not in retval: print 'Error adding ' + track['title'] + ' - ' + track['artist'] + ' (' + track['album'] + ')' else: dst_track = heuristic_search(library2, track, args.strict_heuristics) if dst_track is not None: api2.add_songs_to_playlist(dst_playlist_id, dst_track['id']) 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: api2.add_songs_to_playlist(dst_playlist_id, dst_track['id']) else: failed_tracks.append(track) continue print '----------------- FAILED TRACKS --------------------' for track in failed_tracks: print track['title'] + ' - ' + track['artist'] + ' (' + track['album'] + ')'
song_hits = result['song_hits'] print 'Found {} song(s).'.format(len(song_hits)) if len(song_hits) == 0: not_found_songs.append(song) else: song_hit = song_hits[0] # Only add first search result for this song to playlist track = song_hit['track'] msg = u'Adding "{t[artist]} - {t[title]}"'.format(t=track) if 'album' in track.keys(): msg = msg + u' from album "{t[album]}"'.format(t=track) if 'year' in track.keys(): msg = msg + u' ({t[year]})'.format(t=track) print msg print storeIds.append(track['storeId']) # Create Google Play Music playlist and insert songs playlist_name = '1live {:%Y-%m-%d %H:%M:%S}'.format(datetime.now()) playlist_id = api.create_playlist(playlist_name) api.add_songs_to_playlist(playlist_id, storeIds) print 'Added {} songs to playlist "{}".'.format(len(storeIds), playlist_name) print '{} songs were extracted initially.'.format(len(songs), url) print print 'Songs not found in Google Play Music:' for song in not_found_songs: print u'{s[artist]} - {s[title]}'.format(s=song)
class MusicSync(object): def __init__(self, email=None, password=None): self.mm = Musicmanager() self.wc = Webclient() self.mc = Mobileclient() if not email: email = raw_input("Email: ") if not password: password = getpass() self.email = email self.password = password self.logged_in = self.auth() print "Fetching playlists from Google..." self.playlists = self.mc.get_all_user_playlist_contents() #self.playlists = self.mc.get_all_playlists() #self.playlists = self.wc.get_all_playlist_ids(auto=False) self.all_songs = self.mc.get_all_songs() #print "Got %d playlists." % len(self.playlists['user']) print "Got %d playlists containing %d songs." % (len( self.playlists), len(self.all_songs)) print "" def auth(self): self.logged_in = self.mc.login(self.email, self.password) #self.logged_in = self.wc.login(self.email, self.password) if not self.logged_in: print "Login failed..." exit() print "" print "Logged in as %s" % self.email print "" if not os.path.isfile(OAUTH_FILEPATH): print "First time login. Please follow the instructions below:" self.mm.perform_oauth() self.logged_in = self.mm.login() if not self.logged_in: print "OAuth failed... try deleting your %s file and trying again." % OAUTH_FILEPATH exit() print "Authenticated" print "" def sync_playlist(self, filename, remove_missing): #def sync_playlist(self, filename, remove_missing=False): filename = self.get_platform_path(filename) os.chdir(os.path.dirname(filename)) title = os.path.splitext(os.path.basename(filename))[0] print "Syncing playlist: %s" % filename #if title not in self.playlists['user']: #print " didn't exist... creating..." #self.playlists['user'][title] = [self.wc.create_playlist(title)] print "" plid = "" for pl in self.playlists: if pl['name'] == title: plid = pl['id'] goog_songs = pl['tracks'] if plid == "": print " didn't exist... creating..." plid = self.mc.create_playlist(self, title) #plid = self.playlists['user'][title][0] #goog_songs = self.wc.get_playlist_songs(plid) print "%d songs already in Google Music playlist" % len(goog_songs) pc_songs = self.get_files_from_playlist(filename) print "%d songs in local playlist" % len(pc_songs) print "" # Sanity check max 1000 songs per playlist if len(pc_songs) > MAX_SONGS_IN_PLAYLIST: print " Google music doesn't allow more than %d songs in a playlist..." % MAX_SONGS_IN_PLAYLIST print " Will only attempt to sync the first %d songs." % MAX_SONGS_IN_PLAYLIST del pc_songs[MAX_SONGS_IN_PLAYLIST:] existing_files = 0 added_files = 0 failed_files = 0 removed_files = 0 fatal_count = 0 for fn in pc_songs: if self.file_already_in_list(fn, goog_songs, self.all_songs): existing_files += 1 continue print "" print "Adding: %s" % os.path.basename(fn).encode('cp1252') #print "Adding: %s" % os.path.basename(fn) #online = False online = self.find_song(fn, goog_songs, self.all_songs) #online = self.find_song(fn) song_id = None if online: song_id = online['id'] print " already uploaded [%s]" % song_id else: attempts = 0 result = [] while not result and attempts < MAX_UPLOAD_ATTEMPTS_PER_FILE: print " uploading... (may take a while)" attempts += 1 try: result = self.mm.upload(fn) except (BadStatusLine, CannotSendRequest): # Bail out if we're getting too many disconnects if fatal_count >= MAX_CONNECTION_ERRORS_BEFORE_QUIT: print "" print "Too many disconnections - quitting. Please try running the script again." print "" exit() print "Connection Error -- Reattempting login" fatal_count += 1 self.wc.logout() self.mc.logout() self.mm.logout() result = [] time.sleep(STANDARD_SLEEP) except: result = [] time.sleep(STANDARD_SLEEP) try: if result[0]: song_id = result[0].itervalues().next() else: song_id = result[1].itervalues().next() print " upload complete [%s]" % song_id except: print " upload failed - skipping" tag = self.get_id3_tag(fn) print " failed song:\t%s\t%s\t%s" % ( tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252')) if not song_id: failed_files += 1 continue added = self.mc.add_songs_to_playlist(plid, song_id) time.sleep(.3) # Don't spam the server too fast... print " done adding to playlist" added_files += 1 if remove_missing: for g in goog_songs: for s in self.all_songs: if g['trackId'] == s['id']: print "" print "Removing: %s" % s['title'].encode('cp1252') self.mc.remove_entries_from_playlist(g['id']) #self.wc.remove_songs_from_playlist(plid, s.id) time.sleep(.3) # Don't spam the server too fast... removed_files += 1 print "" print "---" print "%d songs unmodified" % existing_files print "%d songs added" % added_files print "%d songs failed" % failed_files print "%d songs removed" % removed_files def get_files_from_playlist(self, filename): files = [] f = codecs.open(filename, encoding='cp1252') #f = codecs.open(filename, encoding='utf-8') for line in f: line = line.rstrip().replace(u'\ufeff', u'') if line == "" or line[0] == "#": continue path = os.path.abspath(self.get_platform_path(line)) if not os.path.exists(path): print "File not found: %s" % line continue files.append(path) f.close() return files def file_already_in_list(self, filename, goog_songs, all_songs): tag = self.get_id3_tag(filename) print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252')) i = 0 while i < len(goog_songs): for s in all_songs: if goog_songs[i]['trackId'] == s['id']: if self.tag_compare(s, tag): print "Found match\t%s\t%s\t%s" % ( s['title'].encode('cp1252'), s['artist'].encode('cp1252'), s['album'].encode('cp1252')) goog_songs.pop(i) return True i += 1 return False def get_id3_tag(self, filename): data = mutagen.File(filename, easy=True) r = {} if 'title' not in data: title = os.path.splitext(os.path.basename(filename))[0] print 'Found song with no ID3 title, setting using filename:' print ' %s' % title print ' (please note - the id3 format used (v2.4) is invisible to windows)' data['title'] = [title] data.save() r['title'] = data['title'][0] r['track'] = int(data['tracknumber'][0].split('/') [0]) if 'tracknumber' in data else 0 # If there is no track, try and get a track number off the front of the file... since thats # what google seems to do... # Not sure how google expects it to be formatted, for now this is a best guess if r['track'] == 0: m = re.match("(\d+) ", os.path.basename(filename)) if m: r['track'] = int(m.group(0)) r['artist'] = data['artist'][0] if 'artist' in data else '' r['album'] = data['album'][0] if 'album' in data else '' return r def find_song(self, filename, goog_songs, all_songs): tag = self.get_id3_tag(filename) print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252')) #results = self.wc.search(tag['title']) # NOTE - diagnostic print here to check results if you're creating duplicates #print results['song_hits'] #for r in goog_songs: #for r in results['song_hits']: for s in all_songs: #if r['trackId'] == s['id']: if self.tag_compare(s, tag): # TODO: add rough time check to make sure its "close" print "Found match\t%s\t%s\t%s" % ( s['title'].encode('cp1252'), s['artist'].encode('cp1252'), s['album'].encode('cp1252')) return s return None def tag_compare(self, g_song, tag): # If a google result has no track, google doesn't return a field for it if 'title' not in g_song: g_song['title'] = "" if 'artist' not in g_song: g_song['artist'] = "" if 'album' not in g_song: g_song['album'] = "" if 'track' not in g_song: g_song['track'] = 0 if (g_song['title'].lower() == tag['title'].lower() and g_song['artist'].lower() == tag['artist'].lower()) or\ (g_song['album'].lower() == tag['album'].lower() and g_song['title'].lower() == tag['title'].lower()) or\ (g_song['artist'].lower() == tag['artist'].lower() and g_song['album'].lower() == tag['album'].lower() and g_song['track'] == tag['track']): print "Partial match\t%s\t%s\t%s" % ( g_song['title'].encode('cp1252'), g_song['artist'].encode('cp1252'), g_song['album'].encode('cp1252')) return g_song['title'].lower() == tag['title'].lower() and\ g_song['artist'].lower() == tag['artist'].lower() and\ g_song['album'].lower() == tag['album'].lower() #and\ #g_song['track'] == tag['track'] def delete_song(self, sid): self.mc.delete_songs(sid) print "Deleted song by id [%s]" % sid def get_platform_path(self, full_path): # Try to avoid messing with the path if possible if os.sep == '/' and '\\' not in full_path: return full_path if os.sep == '\\' and '\\' in full_path: return full_path if '\\' not in full_path: return full_path return os.path.normpath(full_path.replace('\\', '/'))
explicit, args.explicit): #If the length indicates we're working with a real song, then go ahead and cautiously grab the information we think we need. Not all tracks have all this information. try: store_id = track["storeId"] except: print("No Store ID for Query") try: artist_id = track["artistId"] except: print("No Artist ID for Query") try: #Add the song to Play Music library mc.add_store_tracks([store_id]) mc.add_songs_to_playlist(playlist_id, store_id) print("Added song: \"" + title + "\" by " + artist + " to playlist: " + playlist_name) print("Song artist: " + artist + ", album_artist: " + album_artist) #Break out of the loop cause we don't need to check any of the other results break except: print("Unable to add song: \"" + title + "\" by " + artist + " to playlist: " + playlist_name) #Don't break out of the loop if we were unable to add this song, since then we can keep going through the rest of the songs in the list in case we find one we CAN add else: print("No results found, moving to next search query") else:
secret.CATEGORY_A, secret.CATEGORY_B) ] big_playlist = [ p for p in playlists_to_use if p['name'] == secret.BIG_PLAYLIST ][0] uncategorized = [ p for p in playlists_to_use if p['name'] == secret.UNCATEGORIZED ][0] category_a = [p for p in playlists_to_use if p['name'] == secret.CATEGORY_A][0] category_b = [p for p in playlists_to_use if p['name'] == secret.CATEGORY_B][0] # Create sets of song ids all_songs_set = {song['trackId'] for song in big_playlist['tracks']} cat_a_songs = {song['trackId'] for song in category_a['tracks']} cat_b_songs = {song['trackId'] for song in category_b['tracks']} # Set of all categorized songs categorized_songs = cat_a_songs | cat_b_songs uncategorized_songs = all_songs_set - categorized_songs uncategorized_songs = list(uncategorized_songs) # Add songs to playlist uncategorized_id = uncategorized['id'] print("Adding uncategorized songs to playlist named", secret.UNCATEGORIZED) api.add_songs_to_playlist(uncategorized_id, uncategorized_songs) print("Success! Added", len(uncategorized_songs), "songs to playlist named", secret.UNCATEGORIZED) api.logout() print("Logged out")
count=0 old_playlist_id="" songs = api.get_all_songs() query = ("select a.album_name, s.song_name, s.filename, p.playlist_name from playlist p, playlist_song ps, song s, album a where p.playlist_id = ps.playlist_id and s.album_id = a.album_id and ps.song_id = s.song_id order by p.playlist_name") playlistsongcursor.execute(query) #### TODO - album_name and song_name is not unique... for (album_name, song_name, filename, playlist_name) in playlistsongcursor: for song in songs: if song['album'] == album_name and song['title'] == song_name: # Get the playlist id we are adding to playlists = api.get_all_playlists() for playlist in playlists: if count == 0: old_playlist_id = playlist['id'] count=1; if playlist['name'] == playlist_name: playlist_id = playlist['id'] if playlist_id != old_playlist_id: # We have a complete playlist add it print("Commiting playlist:", [old_playlist_id]) api.add_songs_to_playlist(old_playlist_id, playlistsongs) old_playlist_id = playlist_id playlistsongs = [] print("Adding song:", song['title'], [song['id']], "to playlist:", playlist_name, [playlist_id]) playlistsongs.append(song['id']) # Add the last playlist api.add_songs_to_playlist(playlist_id, playlistsongs) playlistsongcursor.close()
if (f[2] == 0 or (f[3] == False and song['rating'] == '5')): if (song['artist'] == f[0] and song['album'] == f[1] and song['playCount'] == i): bad = False for title in f[4]: if title == song['title']: bad = True if bad == False: f[2] = song['id'] if song['rating'] == '5': f[3] = True print(song['artist']) except: if (song['artist'] == f[0] and song['album'] == f[1] and song['playCount'] == i and f[2] == 0): f[2] = song['id'] print('happen') pass for f in fast: if f[2] != 0: api.add_songs_to_playlist(fastId, f[2]) for f in slow: if f[2] != 0: api.add_songs_to_playlist(slowId, f[2]) print("This line will be printed.") if __name__ == '__main__': pass
if not client.login(args.username, pw, Mobileclient.FROM_MAC_ADDRESS): print("Authentication failed. Please check the provided credentials.") with open(args.source) as f: data = json.load(f) if args.dryrun: print("[/!\] We're currently running in dry-run mode") for playlist in data["playlists"]: if args.dryrun: print("Checking importability of %s" % playlist["title"]) else: print("Importing %s" % playlist["title"]) toimport = [] for track in playlist["tracks"]: query = "%s %s" % (track["title"], track["artist"]) results = client.search(query) match = None if args.verbose: print("Fetching matches for %s" % query) for hit_i, hit in enumerate(results["song_hits"]): trackDeets = hit["track"]["title"] match = hit["track"]["storeId"] print("Found match:\n%s" % trackDeets) break if match is not None: toimport.append(match) else: print("[!!!] No good match for %s" % query) if not args.dryrun and toimport: playlist_id = client.create_playlist(playlist["title"]) client.add_songs_to_playlist(playlist_id, toimport)
def reverseplaylist(playlist_name='', repeat=False, quick=False): mc = Mobileclient() mc.__init__() # Find location of this script dir_path = os.path.dirname(os.path.realpath(__file__)) # Check whether this is the first time the program has been run by searching the directory for the log file file_list = os.listdir(dir_path) # No log file means not run if '.gmusiclog.txt' not in file_list: print( '\n' + 'This is the first time this program has been run in this folder.' + '\n' + 'performing initial authentication.' + '\n') # Autorepeat cannot be true without logfile, override if true repeat = False # Start with a bogus device ID to determine real id from error message devID = 'ffffffff' mc.perform_oauth() try: mc.oauth_login(devID) except Exception as e: error = str(e) IDpos = error.find('*') nextIDpos = error.find('\n', IDpos + 1, len(error)) devID = error[IDpos + 2:nextIDpos] # Perform login mc.oauth_login(devID) # Write authentication stuff to logfile with open(dir_path + '/.gmusiclog.txt', 'w') as log: log.write(devID + '\n') x = datetime.datetime.now() timeString = (str(x.day) + '/' + str(x.month) + '/' + str(x.year) + ' ' + str(x.hour) + ':' + str(x.minute) + ':' + str(x.second) + '.' + str(x.microsecond)) log.write('Initial authentication performed at ' + timeString + '\n') # Log file exists, we will check whether the user has requested autorepeat else: print( '\n' + 'This is not the first time this program has been run in this folder' + '\n' + 'performing login.' + '\n') # Open the logfile to read device id and previous playlists with open(dir_path + '/.gmusiclog.txt', 'r') as log: # Get device ID devID = log.readline().strip() # Look for the playlist name from the bottom of the list contents = log.read() # Perform login mc.oauth_login(devID) playlistLocation = contents.rfind('PLAYLIST') if playlistLocation != -1: # Define end of playlist to make defining desired playlist a little cleaner endOfPlaylist = contents.find('\n', playlistLocation, len(contents)) desired_playlist = contents[playlistLocation + 10:endOfPlaylist] with open(dir_path + '/.gmusiclog.txt', 'a+') as log: # Write authentication stuff to logfile x = datetime.datetime.now() timeString = (str(x.day) + '/' + str(x.month) + '/' + str(x.year) + ' ' + str(x.hour) + ':' + str(x.minute) + ':' + str(x.second) + '.' + str(x.microsecond)) log.write('Login performed at ' + timeString + '\n') # Get user input for desired playlist if autorepeat is not enabled if repeat == False and playlist_name == '': desired_playlist = input( 'Name of the playlist being reversed (case sensetive): ') elif playlist_name != '': # If a name is given this overrides all else desired_playlist = playlist_name else: print('Autorepeat enabled, reversing playlist: ' + desired_playlist + '\n') # Establish the name of the reversed playlist reversed_playlist = desired_playlist + 'REVERSED' # Check to see whether the desired and reversed playlists exist yet allPlaylists = mc.get_all_playlists() desired_playlist_index = -1 reversed_playlist_index = -1 for n in allPlaylists: if n['name'] == reversed_playlist: reversed_playlist_index = n reversedID = n['id'] elif n['name'] == desired_playlist: desired_playlist_index = n desiredID = n['id'] # Desired playlist exists, so we check to see if it has also been reversed if desired_playlist_index != -1: # We cache the playlist name so that it can be automatically re-reversed next time with open(dir_path + '/.gmusiclog.txt', 'a+') as log: log.write('PLAYLIST: ' + desired_playlist + '\n') # Desired playlist has been reversed, we can either delete the old one before proceeding # or perform a quick update if reversed_playlist_index != -1: print('The ' + desired_playlist + ' playlist has been reversed.') # Determine whether to do a quick update or not if quick == True: print('performing quick update') quick_update(mc, desiredID, reversedID) return else: print('Performing full update\n' + 'Deleting the old playlist...\n') mc.delete_playlist(reversedID) # Desired playlist has not been reversed, create the reverse else: print('The ' + desired_playlist + ' playlist has not been reversed, creating the reverse now.') # If we have got this far, the reversed playlist doesn't exist print('Generating reversed song list...') reversedID, id_list = create_new(mc, desired_playlist) print('Adding songs to the playlist...') mc.add_songs_to_playlist(reversedID, id_list) print('Done!') # No such playlist exists else: print( 'No playlist by the name of ' + desired_playlist + ' found. Did you spell it correctly? A reminder here that the playlist name is case sensetive.' )
# an artist/track pair from. for search in plain_search: match = plain_search_match(search) if match is None: no_matches.append((search, search)) else: if match not in songs: songs.append(match) return songs, no_matches if logged_in: logger.info("Logged in") for subReddit, parserConfigurations in config.subreddits.iteritems(): logger.info("Generating songs for " + subReddit) # A subreddit may have multiple playlist configurations. for configuration in parserConfigurations: validate_configuration(configuration) posts = find_posts_in_subreddit(subReddit, configuration) songs, no_matches = find_songs_from_submissions(posts, configuration) playlist_id = create_or_update_playlist(configuration) logger.info("Number of songs matched: {}".format(len(songs))) logger.info("No matches for: {}".format(no_matches)) gmusic.add_songs_to_playlist(playlist_id, songs)
if path_data[path_piece] == 'user': spotify_user_id = path_data[path_piece + 1] if path_data[path_piece] == 'playlist': spotify_playlist_id = path_data[path_piece + 1] # authenticate gapi = Mobileclient() logged_in = gapi.login(g_username, g_password, Mobileclient.FROM_MAC_ADDRESS) client_credentials_manager = spotipy.oauth2.SpotifyClientCredentials( client_id=SPOTIFY_CLIENT_ID, client_secret=SPOTIFY_CLIENT_SECRET) sp = spotipy.Spotify(client_credentials_manager=client_credentials_manager) # get playlist playlist = sp.user_playlist(spotify_user_id, spotify_playlist_id) track_response = playlist['tracks'] gplaylist = [] for song in track_response['items']: song_search_string = "%s - %s - %s" % (song['track']['artists'][0]['name'], song['track']['name'], song['track']['album']['name']) song_result = gapi.search(song_search_string) gplaylist.append(song_result['song_hits'][0]['track']['storeId']) if g_playlist_name == '': g_playlist_name = playlist['name'] playlist_id = gapi.create_playlist(g_playlist_name, 'Imported From Spotify') gapi.add_songs_to_playlist(playlist_id, gplaylist)
add_search_string(tracks, search_strings) else: print("Can't get token for", username) print(f'Detected {len(search_strings)} songs to transfer.') mm = Mobileclient() mm.perform_oauth() mm.oauth_login(Mobileclient.FROM_MAC_ADDRESS) playlist_id = mm.create_playlist(playlist_name) print(f'Playlist \'{playlist_name}\' created.') found_songs = 0 for row in search_strings: print(f'\t Searching \'{row}\'.') search_result = mm.search(row) songs = search_result.get('song_hits') song_id = None if len(songs) > 0: song_id = songs[0].get('track').get('storeId') found_songs += 1 else: print('Song not found.') continue mm.add_songs_to_playlist(playlist_id, song_id) print(f'Imported {found_songs} songs.')
# mismatch between sound count and data returned by API? print episode['name'].encode("utf-8"), ' songs: ', len(data['songs']), '/', episode['song_count'] # for each song for song in data['songs']: artist_name = song['artist']['name'] song_name = song['name'] if not artist_name or not song_name: continue else: artist_name = artist_name.encode("utf-8") song_name = song_name.encode("utf-8") search_song = "{} - {}".format(artist_name, song_name) # get song ID song_id = get_song_id(search_song) if song_id: song_ids.append(song_id) print # add season of music to playlist print 'Adding to playlist: ' + show api.add_songs_to_playlist(playlist_id, song_ids) # clear song list song_ids[:] = []
class GMusic(object): def __init__(self): self.authenticated = False self.all_access = False self.library_loaded = False self.all_songs = [] self.letters = {} self.artists = {} self.albums = {} self.genres = {} self.tracks_by_letter = {} self.tracks_by_artist = {} self.tracks_by_album = {} self.tracks_by_genre = {} self._device = None self._webclient = Webclient(debug_logging=False) self._mobileclient = Mobileclient(debug_logging=False) self._playlists = [] self._playlist_contents = [] self._stations = [] def _get_device_id(self): if self.authenticated: devices = self._webclient.get_registered_devices() for dev in devices: if dev['type'] == 'PHONE': self._device = dev['id'][2:] break elif dev['type'] == 'IOS': self._device = dev['id'] break def _set_all_access(self): settings = self._webclient._make_call(webclient.GetSettings, '') self.all_access = True if 'isSubscription' in settings[ 'settings'] and settings['settings'][ 'isSubscription'] == True else False def _set_all_songs(self): if len(self.all_songs) == 0: try: self.all_songs = self._mobileclient.get_all_songs() except NotLoggedIn: if self.authenticate(): self.all_songs = self._mobileclient.get_all_songs() else: return [] else: return self.all_songs def authenticate(self, email, password): try: mcauthenticated = self._mobileclient.login(email, password) except AlreadyLoggedIn: mcauthenticated = True try: wcauthenticated = self._webclient.login(email, password) except AlreadyLoggedIn: wcauthenticated = True self.authenticated = mcauthenticated and wcauthenticated self._set_all_access() self._get_device_id() return self.authenticated def load_data(self): self._set_all_songs() for song in self.all_songs: thumb = None letter = song['title'][0] artist = song['artist'] album = song['album'] genre = song['genre'] if 'genre' in song else '(None)' if letter not in self.tracks_by_letter: self.tracks_by_letter[letter] = [] self.letters[letter] = None if artist not in self.tracks_by_artist: self.tracks_by_artist[artist] = [] self.artists[artist] = None if album not in self.tracks_by_album: self.tracks_by_album[album] = [] self.albums[album] = None if genre not in self.tracks_by_genre: self.tracks_by_genre[genre] = [] self.genres[genre] = None track = {'artist': artist, 'album': album} if 'title' in song: track['title'] = song['title'] if 'album' in song: track['album'] = song['album'] if 'artist' in song: track['artist'] = song['artist'] if 'durationMillis' in song: track['durationMillis'] = song['durationMillis'] if 'id' in song: track['id'] = song['id'] if 'trackNumber' in song: track['trackType'] = song['trackNumber'] if 'storeId' in song: track['storeId'] = song['storeId'] if 'albumArtRef' in song: track['albumArtRef'] = song['albumArtRef'] thumb = song['albumArtRef'][0]['url'] self.letters[letter] = thumb self.artists[artist] = thumb self.albums[album] = thumb self.genres[genre] = thumb self.tracks_by_letter[letter].append({ 'track': track, 'thumb': thumb, 'id': song['id'] }) self.tracks_by_artist[artist].append({ 'track': track, 'thumb': thumb, 'id': song['id'] }) self.tracks_by_album[album].append({ 'track': track, 'thumb': thumb, 'id': song['id'] }) self.tracks_by_genre[genre].append({ 'track': track, 'thumb': thumb, 'id': song['id'] }) self.library_loaded = True def get_tracks_for_type(self, type, name): type = type.lower() if type == 'artists': return self.tracks_by_artist[name] elif type == 'albums': return self.tracks_by_album[name] elif type == 'genres': return self.tracks_by_genre[name] elif type == 'songs by letter': return self.tracks_by_letter[name] else: return {} def get_song(self, id): return [x for x in self.all_songs if x['id'] == id][0] def get_all_playlists(self): if len(self._playlists) == 0: try: self._playlists = self._mobileclient.get_all_playlists() except NotLoggedIn: if self.authenticate(): self._playlists = self._mobileclient.get_all_playlists() else: return [] return self._playlists def get_all_user_playlist_contents(self, id): tracks = [] if len(self._playlist_contents) == 0: try: self._playlist_contents = self._mobileclient.get_all_user_playlist_contents( ) except NotLoggedIn: if self.authenticate(): self._playlist_contents = self._mobileclient.get_all_user_playlist_contents( ) else: return [] for playlist in self._playlist_contents: if id == playlist['id']: tracks = playlist['tracks'] break return tracks def get_shared_playlist_contents(self, token): playlist = [] try: playlist = self._mobileclient.get_shared_playlist_contents(token) except NotLoggedIn: if self.authenticate(): playlist = self._mobileclient.get_shared_playlist_contents( token) else: return [] return playlist def get_all_stations(self): if len(self._stations) == 0: try: self._stations = self._mobileclient.get_all_stations() except NotLoggedIn: if self.authenticate(): self._stations = self._mobileclient.get_all_stations() else: return [] return self._stations def get_station_tracks(self, id, num_tracks=200): tracks = [] try: tracks = self._mobileclient.get_station_tracks(id, num_tracks) except NotLoggedIn: if self.authenticate(): tracks = self._mobileclient.get_station_tracks(id, num_tracks) else: return [] return tracks def get_genres(self): genres = [] try: genres = self._mobileclient.get_genres() except NotLoggedIn: if self.authenticate(): genres = self._mobileclient.get_genres() else: return [] return genres def create_station(self, name, id): station = None try: station = self._mobileclient.create_station(name=name, genre_id=id) except NotLoggedIn: if self.authenticate(): station = self._mobileclient.create_station(name=name, genre_id=id) else: return [] return station def search_all_access(self, query, max_results=50): results = None try: results = self._mobileclient.search_all_access(query, max_results) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.search_all_access( query, max_results) else: return [] return results def get_artist_info(self, id, include_albums=True, max_top_tracks=5, max_rel_artist=5): results = None try: results = self._mobileclient.get_artist_info( id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.get_artist_info( id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist) else: return [] return results def get_album_info(self, id, include_tracks=True): results = None try: results = self._mobileclient.get_album_info( id, include_tracks=include_tracks) except NotLoggedIn: if self.authenticate(): results = self._mobileclient.get_album_info( id, include_tracks=include_tracks) else: return [] return results def add_aa_track(self, id): track = None try: track = self._mobileclient.add_aa_track(id) except NotLoggedIn: if self.authenticate(): track = self._mobileclient.add_aa_track(id) else: return None return track def add_songs_to_playlist(self, playlist_id, song_ids): tracks = None try: tracks = self._mobileclient.add_songs_to_playlist( playlist_id, song_ids) except NotLoggedIn: if self.authenticate(): tracks = self._mobileclient.add_songs_to_playlist( playlist_id, song_ids) else: return None return tracks def get_stream_url(self, id): try: stream_url = self._mobileclient.get_stream_url(id, self._device) except NotLoggedIn: if self.authenticate(): stream_url = self._mobileclient.get_stream_url( id, self._device) else: return '' except CallFailure: raise CallFailure('Could not play song with id: ' + id, 'get_stream_url') return stream_url
data = get(season_url) # for each episode for episode in data['episodes']: data = get(season_url+'/'+episode['id']) # mismatch between sound count and data returned by API? print episode['name'].encode("utf-8"), ' songs: ', len(data['songs']), '/', episode['song_count'] # for each song for song in data['songs']: #if(song['confidence'] == 'high'): search_song = song['artist']['name'] + ' - ' + song['name'] # get song ID id = get_song_id(search_song) if(id): song_ids.append(id) #else: # print search_song + ' (not found)' print # add season of music to playlist print 'Adding to playlist: ' + show api.add_songs_to_playlist(playlist_id, song_ids) # clear song list song_ids[:] = []
from gmusicapi import Mobileclient from grooveshark import Client import sys api = Mobileclient() api.login(sys.argv[1], sys.argv[2]) groove_client = Client() playlist = groove_client.playlist(sys.argv[3]) gp_songs=[] for song in playlist.songs: query = song.name + " " + song.artist.name gp_query_result = api.search_all_access( query, 1) try: track = gp_query_result['song_hits'][0]['track'] print( "Adding " + track['title'] + " by " + track['artist'] ) gp_songs.append(track['nid']) except IndexError as e: print("Coudn't find " + query); pass except: print(e) pass gp_playlist = api.create_playlist(playlist.name) api.add_songs_to_playlist(gp_playlist, gp_songs)
class GoogleMusicClient(object): def __init__(self, username, password): self._username = username self._password = password self._apiClient = None def initConnection(self): self._apiClient = Mobileclient(debug_logging=False) if not self._apiClient.login(self._username, self._password): raise RuntimeError("Could not connect %s to Google Music." % \ self._username) def addPlaylist(self, playlist): plid = self._apiClient.create_playlist(playlist.name) tids = [] for track in playlist.tracks: aaid = self._getAllAccessTrackId(track) if aaid: try: tid = self._apiClient.add_aa_track(aaid) if tid: tids.append(tid) else: LOGGER.warning( "Could not add track %s to library.", str(track)) except: LOGGER.error("Could not add track %s to library.", str(track)) continue else: LOGGER.warning("Track %s not found.", str(track)) self._apiClient.add_songs_to_playlist(plid, tids) def addAlbum(self, album): aaid = self._getAllAccessAlbumId(album) if aaid: albumInfo = self._apiClient.get_album_info(aaid, include_tracks=True) for track in albumInfo["tracks"]: try: self._apiClient.add_aa_track(track[Track.GM_ID_KEY]) except: LOGGER.error("Could not add track %s to library.", str(track)) continue else: LOGGER.warning("Album %s not found.", str(album)) def _getAllAccessFirstResult(self, resource): queryStr = re.sub("[-:\(\)\",]","", "%s %s" % (resource.name, resource.artist)) queryStr = re.sub("\s+", "+", queryStr) searchResults = self._apiClient.search_all_access(queryStr) gmusicResources = searchResults.get(resource.GM_HITS_KEY) if gmusicResources: firstResult = gmusicResources[0][resource.GM_NAME] return firstResult[resource.GM_ID_KEY] else: return None def _getAllAccessTrackId(self, track): return self._getAllAccessFirstResult(track) def _getAllAccessAlbumId(self, album): return self._getAllAccessFirstResult(album)
song = song[:special_char] + song[special_char + 1:] special_char = song.find('\\') sTokens = song.split('`') song, duration = sTokens[0], int(sTokens[1]) results = api.search_all_access(song, 10)['song_hits'] song_id = findMatch(results, song, duration, explicit_mode, matchHistory) if song_id is None: results = api.search_all_access(song.split('~')[0], 10)['song_hits'] song_id = findMatch(results, song, duration, explicit_mode, matchHistory) if song_id is not None: if errcount >= 20: raise Exception( 'Too many errors have occurred - the server is not responding properly.' ) while errcount < 20: try: api.add_songs_to_playlist(newPID, song_id) break except gmusicapi.exceptions.CallFailure: traceback.print_exc() print print 'was trying to look up ' + song print 'it seems the api has failed to respond properly - waiting for some time.' time.sleep(2) errcount += 1 else: print 'could not find ' + song
client_secret='YOUR CLIENT SECRET', redirect_uri='YOUR REDIRECT URI') if token: sp = spotipy.Spotify(auth=token) # start and authorize spotipy tracks = get_playlist_tracks( username, playlist) # get track, artist, and album names else: print "Can't get token for", username # ******************** # Update GPM playlist: # ******************** gpm = Mobileclient() gpm_username = '******' gpm_password = '******' logged_in = gpm.login( gpm_username, gpm_password, Mobileclient.FROM_MAC_ADDRESS) # is true if log in successful if logged_in: gpm_song_ids, skipped_songs = get_song_ids(tracks) gpm_playlist_id = 'YOUR GPM PLAYLIST ID' #gpm.create_playlist('Export to GPM Test', description='Test playlist', public=False) for i in range(len(gpm_song_ids)): gpm.add_songs_to_playlist(gpm_playlist_id, gpm_song_ids[i]) outputf.write('Number of tracks not found: %s' % skipped_songs) write_list_to_file(skipped_songs) else: print "Can't log in to GPM. Check credentials or connection."
class Gopma(): def __init__(self, action=None): print "Initialising GOPMA." config = ConfigParser.ConfigParser() config.read('config.ini') email = config.get('login', 'email') password = config.get('login', 'password') try: auth_token = config.get('login', 'auth_token') except: auth_token = False print "No auth token could be found" print "Logging into Google Play Music as", email logged_in = False bad_auth = False while not logged_in: if not auth_token or bad_auth: self.api = Mobileclient() login = self.api.login(email, password, Mobileclient.FROM_MAC_ADDRESS) if not login: print "Login failed, check your credentials." sys.exit() # Save the auth token for later with open('config.ini', 'w+') as f: config.set('login', 'auth_token', self.api.session._authtoken) config.write(f) f.close() print "Saved auth token for later." logged_in = True else: print "Found an auth token, trying it." self.api = Mobileclient() self.api.session._authtoken = auth_token self.api.session.is_authenticated = True try: # Test the auth token self.api.get_registered_devices() logged_in = True except: # Failed print "Bad auth token, manually signing in." bad_auth = True print "Successfully logged in as", email if action != 'reset_genres': print "Loading data." self.playlists = self.api.get_all_playlists() self.content = self.api.get_all_user_playlist_contents() self.root_genres, self.child_genres = self.load_genres() print "Data successfully loaded." def create_or_retrieve_playlists(self, playlists): """ Helper function to create or retrieve playlist IDs for a given agg_lists Input: List of playlist names Output: Dict of playlist names and IDs """ if type(playlists) is not list: print "Stop passing non-lists to this function." sys.exit() agg_lists = [ p for p in self.content if p.get('type') == 'USER_GENERATED' and p.get('name') in playlists ] # Get all playlist IDs agg_playlists = {} existing_playlists = [playlist['name'] for playlist in agg_lists] for name in playlists: if name not in existing_playlists: print "Playlist not found, creating", name agg_playlists[name] = self.api.create_playlist(name) self.api.edit_playlist(agg_playlists[name], public=True) else: print "Playlist found", name + ", retrieving ID." playlist_id = [ p['id'] for p in agg_lists if p.get('name') == name ][0] agg_playlists[name] = playlist_id # self.api.edit_playlist(agg_playlists[name], public=True) return agg_playlists def load_genres(self, reset=False): """ Load all genres """ # Get the root genres if os.path.isfile(ROOT_GENRE_FILE): print "Found a root genres file." if reset: root_genres = self.api.get_genres() with open(ROOT_GENRE_FILE, 'w') as fp: pickle.dump(root_genres, fp) print "Root genres have been reset." else: with open(ROOT_GENRE_FILE) as fp: root_genres = pickle.load(fp) else: print "Couldn't find a root genres file, retrieving data." root_genres = self.api.get_genres() with open(ROOT_GENRE_FILE, 'w') as fp: pickle.dump(root_genres, fp) print "Root genres file created." # Get the child genres if os.path.isfile(CHILD_GENRE_FILE): print "Found a child genres file." if reset: child_genres = {} for genre in root_genres: children = self.api.get_genres(genre['id']) child_names = [] for child in children: child_names.append(child['name']) child_genres[genre['id']] = child_names with open(CHILD_GENRE_FILE, 'w') as fp: pickle.dump(child_genres, fp) print "Child genres have been reset." else: with open(CHILD_GENRE_FILE) as fp: child_genres = pickle.load(fp) else: print "Couldn't find a child genres file, retrieving data." child_genres = {} for genre in root_genres: children = self.api.get_genres(genre['id']) child_names = [] for child in children: child_names.append(child['name']) child_genres[genre['id']] = child_names with open(CHILD_GENRE_FILE, 'w') as fp: pickle.dump(child_genres, fp) print "Child genres file created." return root_genres, child_genres def delete_empty_playlists(self): """ Delete ALL empty playlists. Be careful with this. """ playlists = self.content for playlist in playlists: if len(playlist['tracks'] ) == 0 and playlist['name'] != AGGREGATE_PLAYLIST_NAME: self.api.delete_playlist(playlist['id']) print "Deleted", playlist['name'] def create_playlists(self): """ Create all needed playlists """ print "Creating/updating playlists." self.create_or_retrieve_playlists( [AGGREGATE_PLAYLIST_NAME, SHARED_PLAYLIST_NAME]) self.create_or_retrieve_playlists( [PLAYLIST_PREFIX + genre for genre in GENRE_PLAYLISTS.values()]) def get_playlist_urls(self): """ Get all gopma playlist URLS """ urls = {} for playlist in self.playlists: if PLAYLIST_PREFIX in playlist['name'] and playlist[ 'type'] == 'USER_GENERATED': urls[playlist[ 'name']] = "https://play.google.com/music/playlist/" + playlist[ 'shareToken'] return urls def get_playlist_id(self, name): """ Get the playlist ID for a given playlist name """ playlist = [p for p in self.playlists if p.get('name') == name][0] return playlist['id'] def get_share_token(self, playlist_id): """ Get the share token for a given playlist ID """ playlist = [p for p in self.playlists if p.get('id') == playlist_id] return playlist[0]['shareToken'] def get_playlist_tracks(self, playlist_id): """ Get the tracks for a specified playlist id """ return [p for p in self.content if p.get('id') == playlist_id][0]['tracks'] def get_parent_genre_id(self, genre_name): """ Get the parent id for a given genre name """ # Check the root genres first for genre in self.root_genres: if genre_name == genre['name']: return genre['id'] # Check children genres for gid, genres in self.child_genres.items(): for genre in genres: if genre == genre_name: return gid def wipe_all_playlists(self): """ Wipe all Gopma playlists """ for playlist in self.playlists: if PLAYLIST_PREFIX in playlist[ 'name'] and SHARED_PLAYLIST_NAME not in playlist['name']: print "Wiping playlist: ", playlist['name'] self.wipe_playlist(playlist['id']) def wipe_playlist(self, playlist_id): """ Wipe a given playlist """ playlist_tracks = self.get_playlist_tracks(playlist_id) song_ids = [track['id'] for track in playlist_tracks] self.api.remove_entries_from_playlist(song_ids) def reset_daily_playlists(self): """ Reset the daily playlists """ # Get playlists agg_playlists = self.create_or_retrieve_playlists([TODAY, YESTERDAY]) yest_id = agg_playlists[YESTERDAY] today_id = agg_playlists[TODAY] # Wipe yesterday print "Wiping yesterday's playlist." self.wipe_playlist(yest_id) # Copy today to yesterday print "Copying", TODAY, "to", YESTERDAY today_tracks = self.get_playlist_tracks(today_id) self.api.add_songs_to_playlist(yest_id, [t['trackId'] for t in today_tracks]) # Wipe today print "Wiping today's playlist." self.wipe_playlist(today_id) def update_group_playlist(self): """ Update the big group aggregate and the daily playlist with any new shared songs """ # Get the aggregate playlist songs agg_token = self.get_share_token( self.get_playlist_id(AGGREGATE_PLAYLIST_NAME)) agg_playlists = [ p for p in self.playlists if p.get('type') == 'USER_GENERATED' and p.get('shareToken') == agg_token ] agg_id = agg_playlists[0]['id'] # Get tracks agg_tracks = self.api.get_shared_playlist_contents(agg_token) agg_tracks_ids = [track['trackId'] for track in agg_tracks] print "Updating group playlists." # Get the playlists we want to update with shared_lists = [ p for p in self.playlists if p.get('name') == SHARED_PLAYLIST_NAME ] for playlist in shared_lists: shared_tracks = self.api.get_shared_playlist_contents( playlist['shareToken']) print "\nRetrieving from", playlist[ 'name'], "by", playlist['ownerName'] + ":" # Add songs to aggregate playlist if len(shared_tracks) == 0: print "<< Playlist is empty. >>" else: no_new = True for track in shared_tracks: if track['trackId'] not in agg_tracks_ids: # Add to giant aggregate playlist self.api.add_songs_to_playlist(agg_id, track['trackId']) # Add to daily playlist self.api.add_songs_to_playlist( self.get_playlist_id(TODAY), track['trackId']) # Add to genre relevant playlist self.api.add_songs_to_playlist( self.get_playlist_id( PLAYLIST_PREFIX + GENRE_PLAYLISTS[self.get_parent_genre_id( track['track']['genre'])]), track['trackId']) title = track['track']['title'].encode( 'ascii', 'ignore') artist = track['track']['artist'].encode( 'ascii', 'ignore') print "+", title, "by", artist, "has been added." no_new = False if no_new: print "<< There are no new tracks to be added from this playlist. >>" print "Finished updating group playlists." def update_songs(self): """ Update the database with song information """ # Connect to the database config = ConfigParser.ConfigParser() config.read('config.ini') dbname = config.get('database', 'dbname') dbuser = config.get('database', 'user') dbhost = config.get('database', 'host') dbpass = config.get('database', 'password') try: conn = psycopg2.connect("dbname=" + dbname + " user="******" host=" + dbhost + " password="******"<< Could not connect to the db. >>" print e sys.exit() # Update songs for c in self.content: if c.get('type') == 'USER_GENERATED' and c.get( 'name') == AGGREGATE_PLAYLIST_NAME: songs = c.get('tracks') for s in songs: # Song details details = s['track'] # Date song was added date_added = datetime.fromtimestamp( int(s.get('creationTimestamp')) / 1000000) # Values to save values = [ str(s.get('trackId')), str(details.get('title').encode('UTF-8', 'ignore')), str(details.get('artist').encode('UTF-8', 'ignore')), str(details.get('album').encode('UTF-8', 'ignore')), str(details.get('genre')), date_added ] # SQL query insert_song = "INSERT INTO playlists_song VALUES (%s, %s, %s, %s, %s, %s) ON CONFLICT (tid) DO NOTHING;" # Save to DB self.commit_changes(conn, cur, insert_song, values) print "Songs successfully updated." # Close connection cur.close() conn.close() def commit_changes(self, conn, cur, query, values): try: # Commit our changes made cur.execute(query, values) conn.commit() except psycopg2.Error as exc: print exc sys.exit()
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"]])
tempList.append(filledPlaylist[k][x]) while len(tempList) > 0: selected = choice(tempList) mergedPlaylist.append(selected) tempList.remove(selected) print('done') print('Saving playlist... ', end='') finalPlaylist = [] localBumperPool = [] segmentCount = 0 for i in mergedPlaylist: finalPlaylist.append(i) segmentCount += 1 if segmentCount > 3: segmentCount = 0 if len(localBumperPool) == 0: localBumperPool = bumpersPool[:] selected = choice(localBumperPool) finalPlaylist.append(selected) localBumperPool.remove(selected) finalPlaylist.append(choice(signoffPool)) newPlaylist = api.create_playlist('JRay-FM ' + datetime.now().strftime('%m/%d/%Y %H:%M:%S')) api.add_songs_to_playlist(newPlaylist, finalPlaylist) api.logout() print('done')
class Playlist(): api = None dryrun = False def __init__(self, credentials_path = DEFAULT_CREDS, user = None, dryrun = False, **kwargs): self.dryrun = dryrun self.api = Mobileclient() password = None if not user: user, password = self._loadCredentials(credentials_path) else: password = self._queryPass(user) logged_in = self.api.login(user, password) if not logged_in: print("Login failed, exiting") sys.exit(1) def _queryPass(self, user): return getpass("Provide password for user %s: " % user) def _loadCredentials(self, path = DEFAULT_CREDS): credentials = anymarkup.parse_file(path) return credentials["credentials"]["user"], credentials["credentials"]["password"] def _searchForSong(self, song_item): query = "%(artist)s %(title)s" % song_item result = self.api.search_all_access(query, 10) print("Query: %s, found %d hits" % (query, len(result["song_hits"]))) first = None for item in result["song_hits"]: track = item["track"] if not first: first = track if (song_item["title"] in track["title"] or track["title"] in song_item["title"]) and (song_item["artist"] in track["albumArtist"] or\ song_item["artist"] in track["artist"] or\ track["artist"] in song_item["artist"] or\ track["albumArtist"] in song_item["artist"]\ ): return track if first: print("=> Given title and artist don't match, using first result: %s - %s" % (first["title"], first["artist"])) return first return None def _createPlaylist(self): name = "BBC Radio 1 Chart %s" % time.strftime("%Y-%m-%d") return self.api.create_playlist(name, public=True) def _addToPlaylist(self, playlist_id, song_item): self.api.add_songs_to_playlist(playlist_id, song_item["nid"]) def create(self, songs_list): if not self.dryrun: playlist_id = self._createPlaylist() for song_item in songs_list: song = self._searchForSong(song_item) if not song: print("=> Couldn't find %s" % song_item) continue if not self.dryrun: self._addToPlaylist(playlist_id, song)
from gmusicapi import Mobileclient from pprint import pprint api = Mobileclient() api.login('', '', Mobileclient.FROM_MAC_ADDRESS) playlist_name = 'ToListenTo' answer=api.search_all_access('', max_results=1) sweet_track_ids = [] artist_id=answer['artist_hits'][0]['artist']['artistId'] response = api.get_artist_info(artist_id, include_albums=False, max_top_tracks=3, max_rel_artist=0) for song in response['topTracks']: sweet_track_ids.append(song['nid']) playlists = api.get_all_playlists() playlist_id = None for playlist in playlists: if playlist_name in playlist['name']: playlist_id = playlist['id'] if not playlist_id: playlist_id = api.create_playlist(playlist_name) api.add_songs_to_playlist(playlist_id, sweet_track_ids)
class GMusic(object): def __init__(self): self.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))
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 MusicPlayer(object): def __init__(self): self.playlist = [] # Array of all tracks self.playlist_id = 0 # Id of playlist self.current_track_index = 0 # Index of current song self.player = Player() # MPlayer instance self.webclient = Webclient() # Client for WebInterface self.mobileclient = Mobileclient() # Client for MobileInterface self.timer = None # Timer to start next track self.deviceid = 0 # DeviceId to use self.playtype = PlayType.LINEAR # LINEAR or SHUFFLE def login(self, username, password): """ Login to Google Music. Keyword arguments: username -- the username password -- the password Returns: True if successful else False """ # If either the web client or the mobile client failed to login return False if not self.webclient.login(username, password) or not self.mobileclient.login(username, password): return False # Use first found devices as ID devices = self.webclient.get_registered_devices(); # Convert HEX to INT self.deviceid = int(devices[0]['id'], 16) return True def load_playlist(self, playlist_name): # Load playlist for playlist in self.mobileclient.get_all_user_playlist_contents(): if playlist['name'] == playlist_name: for track_obj in playlist['tracks']: track_obj['track']['id'] = track_obj['id'] self.playlist.append(track_obj['track']) # Set playlist_id self.playlist_id = playlist['id'] break; # If playlist has not been found, create it if self.playlist_id == 0: self.playlist_id = self.mobileclient.create_playlist(playlist_name) def add_track_to_playlist(self, track): """ Append a track to the end of playlist Keyword arguments: track -- a dictionary containing the track informations """ track_id = self.mobileclient.add_songs_to_playlist(self.playlist_id, track['nid'])[0] track['id'] = track_id self.playlist.append(track) # Notify all clients about the new track factory.forwarder.dispatch(PLAYLIST_EVENT_TRACK_ADDED, json.dumps(track)) def remove_track_from_playlist(self, track_id): """ Removes a track from the playlist Keyword arguments: track_id -- The id of the track to remove """ self.mobileclient.remove_entries_from_playlist(track_id) index_to_remove = self._find_index_of_track_id(track_id) del self.playlist[index_to_remove] factory.forwarder.dispatch(PLAYLIST_EVENT_TRACK_REMOVED, track_id) def play_track(self, track_id): """ Play a track Keyword arguments: track_id -- Id of the track to play """ index_of_track = self._find_index_of_track_id(track_id) track_to_play = self.playlist[index_of_track] if track_to_play is not None: # Request stream url from google music stream_url = self.mobileclient.get_stream_url(track_to_play["storeId"], self.deviceid) # Load stream url to mplayer self.player.loadfile(stream_url) # For some reason OSX needs to unpause mplayer if sys.platform == "darwin": self.player.pause() # Set track self.current_track_index = index_of_track # Cancel previous timer if self.timer is not None: self.timer.cancel() # How many minutes does the track last track_duration = long(track_to_play["durationMillis"]) / 1000 # Set Timer to play next track when trackDuration is over self.timer = Timer(track_duration, self.play_next_track) self.timer.daemon = True self.timer.start() print "playing", track_to_play["artist"], " - ", track_to_play["title"], " : ", stream_url # Fire event that a new track is playing factory.forwarder.dispatch(TRACK_EVENT_PLAYBACK, json.dumps(track_to_play)) return True else: return False def play_next_track(self): """ Play the next track in the playlist. Returns: True or False """ if self.playtype == PlayType.LINEAR: # Index of next track to play next_track_index = self.current_track_index + 1 # Restart at index 0 if end of playlist is reached if next_track_index >= len(self.playlist): next_track_index = 0 elif self.playtype == PlayType.SHUFFLE: # Index of next track to play at random next_track_index = random.randrange(0, len(self.playlist), 1) # Obtain the id of the next track to play next_track_id = self.playlist[next_track_index]['id'] # Play track with that id return self.play_track(next_track_id) def play_previous_track(self): """ Play the previous track in the playlist. Returns: True or False """ if self.playtype == PlayType.LINEAR: # Index of previous track to play previous_track_index = self.current_track_index - 1 # Contiune from the end of the playlist if previous_track_index <= 0: previous_track_index = len(self.playlist) - 1 elif self.playtype == PlayType.SHUFFLE: # Index of the previous track is random previous_track_index = random.randrange(0, len(self.playlist), 1) # Obtain the id of the previous track to play previous_track_id = self.playlist[previous_track_index]['id'] # Play track with that id return self.play_track(previous_track_id) def stop(self): """ Stop playback. """ if self.timer is not None: self.timer.cancel() if self.player is not None: self.player.stop() def play(self): """ Start playing current track Returns: True if track has been started. Else False """ current_track_id = self.playlist[self.current_track_index] return self.play_track(current_track_id) def _find_index_of_track_id(self, track_id): index = 0 for track in self.playlist: if track['id'] == track_id: return index index += 1 return None
class Gmusic(object): """Class to handle Google Music-related functionality""" def __init__(self, bot): """ init """ self.bot = bot self.mob = Mobileclient() def login(self, username, password, android_id=Mobileclient.FROM_MAC_ADDRESS): """ login method """ self.mob.login(username, password, android_id) return self.mob.is_authenticated() def search(self, searchterms): """ search for stuff """ hits = self.mob.search("{0}".format(searchterms)) return hits def create_playlist(self, name, song_ids, public=True): """ create new playlist named 'name', containing songs with 'song_id' """ playlist_id = self.mob.create_playlist(name, description="Bot Playlist", public=public) self.mob.add_songs_to_playlist(playlist_id, song_ids) return playlist_id def _make_playlist_share_link(self, share_token): base_share_url = "https://play.google.com/music/playlist" return "{}/{}".format(base_share_url, share_token) def share_playlist(self, playlist_id): try: [share_token] = [ plist['shareToken'] for plist in self.mob.get_all_playlists() if plist['id'] == playlist_id ] return self._make_playlist_share_link(share_token) except ValueError: return "Cannot find playlist" def get_best_song_match(self, artist, title): hits = self.search("{0} {1}".format(artist, title)) tracks = self.filter_to_song_minimum_info(self.get_songs(hits)) similarities = [(similarity(track['artist'], artist, track['title'], title), track) for track in tracks] sorted_tracks = sorted(similarities, key=lambda k: k[0]) best_track = None if len(sorted_tracks) > 0: best_track = sorted_tracks[0][1] return best_track def get_best_album_match(self, artist, album): hits = self.search("{0} {1}".format(artist, album)) albums = self.get_albums(hits) similarities = [(similarity(a['artist'], artist, a['album'], album), a) for a in albums] sorted_albums = sorted(similarities, key=lambda k: k[0]) if len(sorted_albums) == 0: return [] best_album = sorted_albums[0][1] album_info = self.mob.get_album_info(best_album['albumId']) store_ids = [t['storeId'] for t in album_info['tracks']] print("Store ids in best_album_match: {0}".format(store_ids)) return store_ids def format_best_match(self, artist, title): track = self.get_best_song_match(artist, title) share_base_url = 'https://play.google.com/music/m/' return "{0} {1} {2} - {3}{4}".format(track['artist'], track['album'], track['title'], share_base_url, track['storeId']) def get_albums(self, results): albums = [album.get('album', None) for album in results['album_hits']] album_details = [{ 'artist': a['artist'], 'album': a['name'], 'albumId': a['albumId'] } for a in albums] return album_details def get_songs(self, results): return [song.get('track', None) for song in results['song_hits']] def filter_to_song_minimum_info(self, results): return [{ 'artist': song.get('artist', None), 'album': song.get('album', None), 'title': song.get('title', None), 'storeId': song.get('storeId', None) } for song in results] def convert_spotify_embed_to_gmusic(self, url): s_list = SpotifyPlaylist(url) title = s_list.title best_matches = [ self.get_best_song_match(i.artist, i.track) for i in s_list.items ] filtered_matches = [i for i in best_matches if i is not None] store_ids = [i.get('storeId') for i in filtered_matches] new_plist = self.create_playlist(title, store_ids) return self.share_playlist(new_plist) def convert_hbih_to_gmusic(self, url): hbih_list = HBIHPlaylist(url) title = hbih_list.title store_ids = [] for item in hbih_list.items: album_store_ids = self.get_best_album_match(item[0], item[1]) print("Adding store ids: {0}".format(album_store_ids)) store_ids.extend(album_store_ids) store_id_set = IndexedSet(store_ids) no_dupes_store_ids = list(store_id_set) new_plist = self.create_playlist(title, no_dupes_store_ids[0:1000]) return self.share_playlist(new_plist) def create_playlist_from_song_names(self, artist, songs): year = datetime.datetime.now().year title = "{} setlist ({})".format(artist, year) best_matches = [self.get_best_song_match(artist, s) for s in songs] filtered_matches = [i for i in best_matches if i is not None] store_ids = [i.get('storeId') for i in filtered_matches] new_plist = self.create_playlist(title, store_ids) return self.share_playlist(new_plist) def get_newest_playlists(self, count=5): """ return 'count' newest playlists """ all_plists = self.mob.get_all_playlists() sorted_plists = sorted(all_plists, key=lambda k: k['lastModifiedTimestamp'], reverse=True) if count > 0: newest_plists = sorted_plists[:count] else: newest_plists = sorted_plists info = [{ 'name': p['name'], 'share': self._make_playlist_share_link(p['shareToken']) } for p in newest_plists] return info def get_all_playlists(self): """ return all playlists """ return self.get_newest_playlists(0) # 0 = return everything def find_playlists(self, searchterm): """ find all playlists that have a name containing 'searchterm' """ all_plists = self.get_all_playlists() all_matches = all_plists all_terms = searchterm.split(' ') for term in all_terms: all_matches = [ p for p in all_matches if p['name'].lower().find(term.lower()) != -1 ] return all_matches
queries.append(q) #Now Google mc = Mobileclient() mc.login(config['email'], config['password'], config['android_device_id']) hits = 0 misses = 0 track_ids = [] failed_queries = [] for q in queries: search = mc.search_all_access(q, max_results=5) g_songs = search['song_hits'] if any(g_songs): sort_by_score = sorted(g_songs, key=itemgetter('score'), reverse=True) #print sort_by_score[0]['track']['storeId'] track_ids.append(sort_by_score[0]['track']['storeId']) hits += 1 else: misses += 1 failed_queries.append(q) print 'Hits: {0} Misses: {1}'.format(hits,misses) if misses > 0: print 'Unable to find a Google Music match for these tracks:' print failed_queries playlist_id = mc.create_playlist(playlist_name) mc.add_songs_to_playlist(playlist_id, track_ids) print 'Playlist "{}" successfully added to Google Music with {} songs'.format(playlist_name, hits)
def allaccessimport(playlist=None, username=None, password=None, dry_run=False): """ Exports a Spotify playlist to stdout or csv. """ if not username or not password: raise CommandError( "Username and password must be provided as either command-line " + "argument or in the application configuration file.") playlist_name = playlist playlist_description = "" if playlist: playlist_name = os.path.basename(playlist_name) playlist_name = os.path.splitext(playlist_name)[0] logging.debug("Playlist name will be: {}".format(playlist_name)) api = Mobileclient(False, False) logged_in = api.login(username, password, Mobileclient.FROM_MAC_ADDRESS) if not logged_in: raise CommandError( 'Error. Unable to login to Google Music All Access.') playlist_ref = None currenttracks = None failed_tracks = list() songs_added = 0 total = 0 stream = open(playlist, "rb") if playlist else sys.stdin for input_line in stream: input_line = input_line.strip() # Lazily search the beginning of the file for a Playlist name if input_line.startswith("#"): data = input_line[1:] parts = [x.strip() for x in data.split(":", 1)] if len(parts) == 2: if parts[0] == "Playlist": playlist_name = parts[1] elif parts[0] == "Description": playlist_description = parts[1] continue if not playlist_ref: if not playlist_name: raise CommandError( "A playlist name was not given and it was not found " + "in the file either. Can't continue.") else: playlist_ref, currenttracks = get_playlist(api, playlist_name) if not playlist_ref and not dry_run: sys.stderr.write('Playlist not found. Creating new.\n') playlist_ref = api.create_playlist( playlist_name, description=playlist_description) yield 'Going to update playlist {0} ({1})\n'.format( playlist_name, playlist_ref) trackinfo = list(csv.reader([input_line], quoting=csv.QUOTE_ALL))[0] if trackinfo[0] == 'Track' and trackinfo[1] == 'Artist': yield 'Skipping header.' continue search_term = "{0} {1}".format(trackinfo[0], trackinfo[1]) total = total + 1 newtrackid, error_reason = search_track(api, search_term, currenttracks) if newtrackid: if not dry_run: #print("Add to {} song {}".format(playlist_ref, newtrackid)) api.add_songs_to_playlist(playlist_ref, [newtrackid]) songs_added = songs_added + 1 else: failed_tracks.append(trackinfo) sys.stderr.write("Searching {}...{}\n".format(search_term, error_reason)) yield "{0} songs added out of {1}. {2} Failed.".format( songs_added, total, total - songs_added) yield "Failed tracks:" for line in failed_tracks: print " ", line