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)
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'))
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 GMusicWS: def __init__(self, user, password, playlistName): self.playlistName = playlistName self.api = Mobileclient() print("Logging into MobileClient API") self.api.login(user, password, "android_id") #insert unique android_id here def mapUnknownTracks(self, db): playlist = db.unmappedTracks() for track in playlist: searchstr = track.artist + " " + track.song print("Searching for %s" % (searchstr)) try: result = self.api.search_all_access(searchstr, max_results=1) print("Found " + result['song_hits'][0]['track']['artist'] + " - " + result['song_hits'][0]['track']['title']) nid = result['song_hits'][0]['track']['nid'] db.storemapping(track.song, track.artist, nid) except: print("Error parsing result: " + str(result)) time.sleep(1) def maintain(self, tracks): print("Searching for playlist %s" % (self.playlistName)) found = False searchres = self.api.get_all_playlists() for list in searchres: if list['name'] == self.playlistName: found = True pid = list['id'] if not found: print("Not found - creating") pid = self.api.create_playlist(self.playlistName) print("Playlist id is %s" % (pid)) print("Getting current contents") playlists = self.api.get_all_user_playlist_contents() currentEntries = [] for playlist in playlists: if playlist['name'] == self.playlistName: for entry in playlist['tracks']: currentEntries.append(entry['id']) print("Removing songs") self.api.remove_entries_from_playlist(currentEntries) print("Adding songs") self.api.add_songs_to_playlist(pid, tracks)
class 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
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 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 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)
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'] + ')'
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
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)
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)
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)
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)
class MusicSync(object): def __init__(self, email=None, password=None): self.mm = Musicmanager() self.wc = Webclient() self.mc = Mobileclient() if not email: email = raw_input("Email: ") if not password: password = getpass() self.email = email self.password = password self.logged_in = self.auth() print "Fetching playlists from Google..." self.playlists = self.wc.get_all_playlist_ids(auto=False) print "Got %d playlists." % len(self.playlists["user"]) print "" def auth(self): self.logged_in = self.wc.login(self.email, self.password) self.logged_in = self.mc.login(self.email, self.password) if not self.logged_in: print "Login failed..." exit() print "" print "Logged in as %s" % self.email print "" if not os.path.isfile(OAUTH_FILEPATH): print "First time login. Please follow the instructions below:" self.mm.perform_oauth() self.logged_in = self.mm.login() if not self.logged_in: print "OAuth failed... try deleting your %s file and trying again." % OAUTH_FILEPATH exit() print "Authenticated" print "" def add_rhapsody_playlist(self, filename, remove_missing=False): filename = self.get_platform_path(filename) os.chdir(os.path.dirname(filename)) # playlist_title = os.path.splitext(os.path.basename(filename))[0] print "Synching File: %s" % filename print "Parsing Songs from %s" % filename pc_songs = self.get_songs_from_file(filename) # print (pc_songs) print "%d songs in local file: %s" % (len(pc_songs), filename) # Sanity check max 1000 songs per playlist if len(pc_songs) > MAX_SONGS_IN_PLAYLIST: print " Google music doesn't allow more than %d songs in a playlist..." % MAX_SONGS_IN_PLAYLIST print " Will only attempt to sync the first %d songs." % MAX_SONGS_IN_PLAYLIST del pc_songs[MAX_SONGS_IN_PLAYLIST:] existing_files = 0 added_files = 0 failed_files = 0 removed_files = 0 fatal_count = 0 for song in pc_songs: playlist_title = song["playlist"] if playlist_title not in self.playlists["user"]: self.playlists["user"][playlist_title] = [self.mc.create_playlist(playlist_title)] time.sleep(0.7) print "Starting Playlist Sync with Google music..." for song in pc_songs: # print song plid = "" print "--------------------------------" print "" print "Playlist: %s" % song["playlist"] print "Artist: %s" % song["artist"] print "Song: %s" % song["title"] print "Album: %s" % song["album"] playlist_title = song["playlist"] plid = self.playlists["user"][playlist_title][0] goog_songs = self.wc.get_playlist_songs(plid) if self.song_already_in_list(song, goog_songs): existing_files += 1 print "Result: Song Already Added" continue print "Total %d songs in Google playlist: %s" % (len(goog_songs), playlist_title) print "%s - %s, didn't exist...Will try to add..." % (song["artist"], song["title"]) print "" print "--------------------------------" results = self.mc.search_all_access(song["title"], max_results=50) nid = self.filter_search_results(results, song) print "AA nId: %s " % nid if nid: song_id = self.mc.add_aa_track(nid) added = self.wc.add_songs_to_playlist(plid, song_id) print "Playlist UUid: %s" % plid print "Song ID: %s" % song_id time.sleep(0.3) # Don't spam the server too fast... print "Result: done adding to playlist" added_files += 1 continue else: query = "%s %s" % (song["artist"], song["title"].split(" ")[0]) print "Query %s" % query results = self.mc.search_all_access(query, max_results=50) nid = self.filter_search_results(results, song) if nid: song_id = self.mc.add_aa_track(nid) added = self.wc.add_songs_to_playlist(plid, song_id) print "Playlist UUid: %s" % plid print "Song ID: %s" % song_id time.sleep(0.3) # Don't spam the server too fast... print " -- done adding to playlist" added_files += 1 continue print "Result: NID Blank, Song not Found in All Access" print "" print "---" print "%d songs unmodified" % existing_files print "%d songs added" % added_files print "%d songs failed" % failed_files print "%d songs removed" % removed_files def get_songs_from_file(self, filename): songs = [] f = codecs.open(filename, encoding="utf-8") for line in f: line = line.rstrip().replace(u"\ufeff", u"") if line == "" or line[0] == "#": continue la = line.split("\\") regex_filter = "[^A-Za-z0-9\,\-\.\ \(\)'\!\?\$\/ \& \:]" artist = re.sub(regex_filter, "", la[1]) playlist = re.sub(regex_filter, "", la[0]) album = re.sub(regex_filter, "", la[2]) title = re.sub(regex_filter, "", la[3]) # print "Filtered Strings:" # print "Artist: %s" % artist # print "Playlist: %s" % playlist # print "Song: %s" % title # print "Album: %s" % album dt = {"playlist": playlist, "artist": artist, "album": album, "title": title} # print (dt) songs.append(dt) f.close() return songs def get_songs_from_playlist(self, filename): songs = [] f = codecs.open(filename, encoding="utf-8") for line in f: line = line.rstrip().replace(u"\ufeff", u"") if line == "" or line[0] == "#": continue path = os.path.abspath(self.get_platform_path(line)) # if not os.path.exists(path): # print "File not found: %s" % line # continue songs.append(path) f.close() return songs def get_files_from_playlist(self, filename): files = [] f = codecs.open(filename, encoding="utf-8") for line in f: line = line.rstrip().replace(u"\ufeff", u"") if line == "" or line[0] == "#": continue path = os.path.abspath(self.get_platform_path(line)) if not os.path.exists(path): print "File not found: %s" % line continue files.append(path) f.close() return files def song_already_in_list(self, song, goog_songs): # tag = self.get_id3_tag(filename) i = 0 while i < len(goog_songs): # print goog_songs if self.tag_compare(goog_songs[i], song): goog_songs.pop(i) return True i += 1 return False def file_already_in_list(self, filename, goog_songs): tag = self.get_id3_tag(filename) i = 0 while i < len(goog_songs): if self.tag_compare(goog_songs[i], tag): goog_songs.pop(i) return True i += 1 return False def get_id3_tag(self, filename): data = mutagen.File(filename, easy=True) r = {} if "title" not in data: title = os.path.splitext(os.path.basename(filename))[0] print "Found song with no ID3 title, setting using filename:" print " %s" % title print " (please note - the id3 format used (v2.4) is invisible to windows)" data["title"] = [title] data.save() r["title"] = data["title"][0] r["track"] = int(data["tracknumber"][0].split("/")[0]) if "tracknumber" in data else 0 # If there is no track, try and get a track number off the front of the file... since thats # what google seems to do... # Not sure how google expects it to be formatted, for now this is a best guess if r["track"] == 0: m = re.match("(\d+) ", os.path.basename(filename)) if m: r["track"] = int(m.group(0)) r["artist"] = data["artist"][0] if "artist" in data else "" r["album"] = data["album"][0] if "album" in data else "" return r def find_song(self, filename, plid): tag = self.get_id3_tag(filename) print "Song Tag: %s " % tag print "Filename: %s" % filename ws_plids = [] ws_plids.append(plid) print (ws_plids) playlists = self.wc.get_all_playlist_ids() print (playlists) results = self.wc.get_playlist_songs(ws_plids) # NOTE - dianostic print here to check results if you're creating duplicates print results print "%s ][ %s ][ %s ][ %s" % (tag["title"], tag["artist"], tag["album"], tag["track"]) for r in results: if self.tag_compare(r, tag): # TODO: add rough time check to make sure its "close" return r return None def filter_search_results(self, results, song): # Try Exact Matching for g_song in results["song_hits"]: if self.tag_compare(g_song["track"], song): return g_song["track"]["nid"] elif self.song_compare(g_song["track"], song, "artist"): # try just the artist return g_song["track"]["nid"] elif self.song_compare(g_song["track"], song, "part-song"): # try part of song and artist return g_song["track"]["nid"] return None def song_compare(self, g_song, tag, type): if "track" not in g_song: g_song["track"] = 0 title_parts = tag["title"].split("(") # removing shit like (featuring wiz) tp = title_parts[0].split(" ") # First word maybe if "artist" in type: return g_song["artist"].lower() == tag["artist"].lower() if "part-song" in type: return g_song["title"].find(tp[0]) and g_song["artist"].lower() == tag["artist"].lower() return None def tag_compare(self, g_song, tag): if "track" not in g_song: g_song["track"] = 0 return ( g_song["title"].split(" ")[0].lower() == tag["title"].split(" ")[0].lower() and g_song["artist"].lower() == tag["artist"].lower() ) def delete_song(self, sid): self.wc.delete_songs(sid) print "Deleted song by id [%s]" % sid def get_platform_path(self, full_path): # Try to avoid messing with the path if possible if os.sep == "/" and "\\" not in full_path: return full_path if os.sep == "\\" and "\\" in full_path: return full_path if "\\" not in full_path: return full_path return os.path.normpath(full_path.replace("\\", "/"))
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)
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 GPM(object): _api = None def __init__(self): self._api = Mobileclient(debug_logging=False) if not self._api.login(GPM_EMAIL_ADDRESS, GPM_APP_PASSWORD, Mobileclient.FROM_MAC_ADDRESS): raise Exception("Failed to authenticate with GPM") def _delete_playlist(self, playlist_id): return self._api.delete_playlist(playlist_id) def _create_playlist(self, name, description): return self._api.create_playlist(name, description) def _add_songs_to_playlist(self, playlist_id, song_ids): return self._api.add_songs_to_playlist(playlist_id, song_ids) def _match_song(self, song): query = f'{song.title} {song.artist}' results = self._api.search(query) hits = [song_hit['track'] for song_hit in results.get('song_hits')] # Check if song is a match for hit in hits: if is_match(hit['title'], song.title) and is_match( hit['artist'], song.artist): return hit['storeId'] return None def _get_playlist(self, name): playlists = self._api.get_all_user_playlist_contents() for playlist in playlists: if playlist['name'] == name: return playlist return None def get_playlist(self, name): playlist = self._get_playlist(name) return Playlist.from_gpm(playlist) if playlist else None def create_playlist(self, playlist, override): gpm_playlist = self.get_playlist(playlist.title) gpm_playlist_id = gpm_playlist.id if gpm_playlist else None # Delete if it already exists if gpm_playlist_id and override: self._delete_playlist(gpm_playlist_id) # Create it if we are re-making it or it didn't already exist if not gpm_playlist_id or override: gpm_playlist_id = self._create_playlist(playlist.title, playlist.description) # Get all song_ids and remove results we could not match matches = [self._match_song(song) for song in playlist.songs] song_ids = [match for match in matches if match is not None] self._add_songs_to_playlist(gpm_playlist_id, song_ids) # Return number of songs added to playlist return len(song_ids)
import os import json from gmusicapi import Mobileclient api = Mobileclient() api.login('*****@*****.**', 'my-password') # => True library = api.get_all_songs() sweet_track_ids = [track['id'] for track in library if track['artist'] == 'The Cat Empire'] playlist_id = api.create_playlist('Rad muzak') api.add_songs_to_playlist(playlist_id, sweet_track_ids)
# try to use album match pl_songs.append(sd["id"]) sd_find = 1 break else: # we assume if the title and artist match, that's the song we're looking for pl_songs.append(sd["id"]) sd_find = 1 break if not sd_find: print "Could not find " + check_title + " by " + check_artist line = f.readline() # create new playlist print 'creating playlist ' + str(need_to_create_name[cnt]) playlist_id = mc.create_playlist(str(need_to_create_name[cnt])) # run through list of songs and add each song_id song_cnt = 0 while (song_cnt < len(pl_songs)): try: mc.add_songs_to_playlist(playlist_id, str(pl_songs[song_cnt])) song_cnt += 1 except: next print str(song_cnt) + " songs added ..." cnt += 1 print str(cnt) + " new playlists added ..." ###################
class Playlister(object): WMBR_SITE = "https://track-blaster.com/wmbr/index.php" WMBR_SITE_NO_PHP = "https://track-blaster.com/wmbr/" PROG_SEARCH_FILL = { "startdt": "June 28, 1984", "enddt": datetime.datetime.now().strftime("%B %d, %Y"), "sort": "desc" } CRED_PATH = os.path.join(str(Path.home()), ".wmbr", "cred") def __init__(self): self.programs = {} site_data = requests.get(self.WMBR_SITE) parser = BeautifulSoup(site_data.content, "html.parser") program_selector = None for selector in parser.find_all("select"): if selector.get("name") == "program": program_selector = selector for option in program_selector.find_all("option"): self.programs[option.string] = option.get("value") self.mb = Mobileclient() if not os.path.exists(os.path.dirname(self.CRED_PATH)): os.mkdir(os.path.dirname(self.CRED_PATH)) if not self.mb.oauth_login(Mobileclient.FROM_MAC_ADDRESS, oauth_credentials=self.CRED_PATH): self.mb.perform_oauth(storage_filepath=self.CRED_PATH) def playlist_visitor(self, lonk): lonk = "".join([self.WMBR_SITE_NO_PHP, lonk]) print("Visting %s" % lonk) lonk_data = requests.get(lonk) lonk_parser = BeautifulSoup(lonk_data.content, "html.parser") row_divs = lonk_parser.find_all("div", {"id": re.compile("row_[0-9]+")}) store_ids = [] for row in row_divs: try: artist_row = row.find_all( "div", class_=re.compile(r"-Artist$") )[1] # gonna get a hidden row with this, actual result should be second in the list song_row = row.find_all( "div", class_=re.compile(r"-Song$") )[1] # gonna get a hidden row with this, actual result should be second in the list except IndexError: # apparently there are rows with no contents or are filled with comments instead continue artist = artist_row.find("a").string song = song_row.find("a").string print("%s : %s" % (artist, song)) try: track_dict = self.mb.search( "%s %s" % (artist, song), max_results=10)['song_hits'][0]['track'] store_ids.append(track_dict['storeId']) except IndexError: print("No results for %s by %s" % (song, artist)) return store_ids def select_a_program(self): playlist_links = set() cli = ScrollBar("Select a show: ", [*self.programs], pointer="🎵", height=6, align=5, margin=3) program = cli.launch() search_fill = self.PROG_SEARCH_FILL.copy( ) # make a copy of the dict in case we want to do another search search_fill['program'] = self.programs[program] search_url = "?".join([self.WMBR_SITE, parse.urlencode(search_fill)]) search_data = requests.get(search_url) search_parser = BeautifulSoup(search_data.content, "html.parser") playlist_a_tags = search_parser.find_all( "a", href=re.compile(r"playlist.php*")) for a in playlist_a_tags: playlist_links.add(a.get('href')) playlist_links = list(playlist_links) songz = [] for lonk in playlist_links: time.sleep(1) songz.extend(self.playlist_visitor(lonk)) playlist_id = self.mb.create_playlist(program) self.mb.add_songs_to_playlist(playlist_id, songz)
action = "" #library = api.get_track_info('Ttk6fmoytroyauv2simicl62hlu') #print data[['storeId','id']] #for i,v in data.iterrows(): # for i2,v2 in enumerate(library): # if v2['storeId'] == data.loc[i, "storeId"]: # print i # data.loc[i, "id"] = v2['id'] # library.pop(i2) # break #data.to_csv("gpmusic_fixed.csv") #print data.describe() #print json.dumps(library[len(library)-1], indent=4, separators=(',', ': ')) #print json.dumps(library, indent=4, separators=(',', ': ')) #device = webapi.get_registered_devices() #streamURL = api.get_stream_url(guid, device[0]['id'][2:]) streamURL = api.get_stream_url(guid, '320b3128904ea650') if action == "": 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(device, indent=4, separators=(',', ': ')) #print webapi.get_stream_urls(library['storeId']) print json.dumps({"streamURL": streamURL, "pid": pid}, indent=4, separators=(',', ': ')) else: print json.dumps({"streamURL": streamURL}, indent=4, separators=(',', ': '))
return True else: print 'unable to login to google music' return False # main if __name__ == '__main__': # input show name show = raw_input('Input show: ').lower() # authenticate to google music if login(): # create playlist playlist_id = api.create_playlist(show) # load seasons in show data = get_show(show.replace(' ', '-')) # song id array (added per season to single playlist) song_ids = [] # for each season for season in data['seasons']: season_url = season['tunefind_api_url'] season_number = season['number'] print show + ' Season: ' + season_number, '\n' data = get(season_url)
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('\\', '/'))
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)
missing.append(row) print "##### {} of {} Songs Found. #####".format(len(songs), len(rows)) log("### Missing Songs ###") for row in missing: log(decode(row['title']) + " could not be found.") log("### Fetching Playlists ###") playlists = api.get_all_user_playlist_contents() playlist = [p for p in playlists if p['name'] == gmusic_playlist_name] song_ids = [s['id'] for s in songs] if len(playlist) == 0: # Playlist not found, create a new one. playlist_id = api.create_playlist(u'foobar2000') else: playlist_id = playlist[0]['id'] # Prevent duplicated songs in existing playlist for track in playlist[0]['tracks']: if track['trackId'] in song_ids: song_ids.remove(track['trackId']) log("Importing to Playlist ID: " + playlist_id) api.add_songs_to_playlist(playlist_id, song_ids) log("### Import Completed ###") logfile.close() api.logout()
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))
sModeYN = raw_input( 'Do you want to enable explicit mode (which, in cases of ambiguity, will select the explicit versions of songs.\n' ) if sModeYN in y: explicit_mode = True else: explicit_mode = False print "If the program cannot uniquely determine which song to import, based on results," + \ "it will present a list of options. To pick, just enter the number of the choice" + \ "you want imported. If none of them properly match, just type in 'none'." matchHistory = {} for k in playlist_map: print '**********************************' print 'now importing ' + k print '**********************************' newPID = api.create_playlist(k) playlist_songs = playlist_map[k] for song in playlist_songs: special_char = song.find('\\') while special_char >= 0: song = song[:special_char] + song[special_char + 1:] special_char = song.find('\\') sTokens = song.split('`') song, duration = sTokens[0], int(sTokens[1]) results = api.search_all_access(song, 10)['song_hits'] song_id = findMatch(results, song, duration, explicit_mode, matchHistory) if song_id is None: results = api.search_all_access(song.split('~')[0], 10)['song_hits'] song_id = findMatch(results, song, duration, explicit_mode,
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)
missing.append(row) print "##### {} of {} Songs Found. #####".format(len(songs), len(rows)) log("### Missing Songs ###") for row in missing: log(decode(row['title']) + " could not be found.") log("### Fetching Playlists ###") playlists = api.get_all_user_playlist_contents() playlist = [p for p in playlists if p['name'] == gmusic_playlist_name] song_ids = [s['id'] for s in songs] if len(playlist) == 0: # Playlist not found, create a new one. playlist_id = api.create_playlist(u'foobar2000') else: playlist_id = playlist[0]['id'] # Prevent duplicated songs in existing playlist for track in playlist[0]['tracks']: if track['trackId'] in song_ids: song_ids.remove(track['trackId']) log("Importing to Playlist ID: " + playlist_id) api.add_songs_to_playlist(playlist_id, song_ids) log("### Import Completed ###") logfile.close() api.logout()
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
songs_ids = [] for line in options.file.readlines(): line = line.strip().decode('utf-8') if not line: continue log.info('Searching for %s', line) result = cached_search(cache, api, line) songs = result['song_hits'] songs = filter_songs(songs, line) if len(songs): songs_ids.append(songs[0]['track']['storeId']) continue log.debug(songs) for pl in api.get_all_playlists(): if pl['name'] == options.playlist: api.delete_playlist(pl['id']) playlist_id = api.create_playlist(options.playlist) try: api.add_songs_to_playlist(playlist_id, songs_ids) except Exception, _: api.delete_playlist(playlist_id)
playlist_to_transfer['id'], fields="tracks,next") tracks = playlist['tracks'] add_search_string(tracks, search_strings) while tracks['next']: tracks = sp.next(tracks) 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.')
track_ids = [get_aa_id(t) for t in matched_tracks if t] # name noname playlists using date created if not playlist_name: log.warning('Replacing blank playlist name with timestamp') playlist_name = playlist_created # skip empty playlists 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)
# name noname playlists using date created if not playlist_name: log.warning('Replacing blank playlist name with timestamp') playlist_name = playlist_created # skip empty playlists 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 ' +
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)
# Check if the playlist exists # If it does, abort unless --replace is specified, in which case delete the old list playlists = gpm.get_all_playlists() for playlist in playlists: if playlist["deleted"]: continue 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:
return True else: print 'unable to login to google music' return False # main if __name__ == '__main__': # input show name show = raw_input('Input show: ').lower() # authenticate to google music if login(): # create playlist playlist_id = api.create_playlist(show) # load seasons in show data = get_show(show.replace(' ', '-')) # song id array (added per season to single playlist) song_ids = [] # for each season for season in data['seasons']: season_url = season['tunefind_api_url'] season_number = season['number'] print show + ' Season: ' + season_number, '\n' data = get(season_url)
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)
artist = textstr.split(',')[0] else: song = textstr.split(',')[0] artist = '' return (artist, song) playlists = api.get_all_playlists() dt = datetime.datetime.now() plname = 'sms_' + str(dt.month) + '_' + str(dt.day) + '_' + str(dt.year) summerlist = False for i in playlists: if plname in i['name'].lower(): summerlist = i['id'] if not summerlist: api.create_playlist(name=plname) api.get_all_playlists() for i in playlists: if plname in i['name'].lower(): summerlist = i['id'] @app.route("/", methods=['GET', 'POST']) def listener(): """Respond to incoming calls with a simple text message.""" resp = twilio.twiml.Response() from_number = request.values.get('From', None) body = request.values.get('Body', None) music = process_sms(body) print"got a text from: " + str(from_number) print "They want to add " + str(music)
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 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('\\', '/'))
#We're going to want to check to make sure that this name isn't already used for a playlist users_playlists_names = [] for user_playlist in users_playlists: users_playlists_names.append(user_playlist["name"]) #Start this number at 2 since that's most likely what a human would do (playlist, playlist2, playlist3, etc) appendable_num = 2 while playlist_name in users_playlists_names: playlist_name = playlist_root_name + " " + str(appendable_num) appendable_num = appendable_num + 1 #Once that loop finishes, playlist_name will be a uniquie-to-user playlist name, and we can go ahead and create that playlist. #So create the actual playlist and keep its id so we can add songs to it easily later playlist_id = mc.create_playlist(name=playlist_name, description="A playlist of nostalgic songs", public=False) #Present some interesting stats to the user about the content on their account print(str(len(owned_songs)) + " songs on account.") #Begin looping through the list of songs from billboard.com for song_to_add in songs_to_add: #Perform the search with the specified criteria artist_name = song_to_add[0] song_title = song_to_add[1] song_already_in_library = 0 library_song_id = None
# If "Uncategorized" playlist already exists, remove all songs from it. # Otherwise create the playlist. if uncategorized_lst: uncategorized_playlist = uncategorized_lst[0] if 'tracks' in uncategorized_playlist: old_uncategorized_songs_set = { song['id'] for song in uncategorized_playlist['tracks'] } old_uncategorized_songs_lst = list(old_uncategorized_songs_set) api.remove_entries_from_playlist(old_uncategorized_songs_lst) print("Cleared all songs from uncategorized playlist named", secret.UNCATEGORIZED) else: api.create_playlist(secret.UNCATEGORIZED) print("Created playlist for uncategorized songs, named", secret.UNCATEGORIZED) # If "Category A"playlist does not exist, create it if not category_a_lst: api.create_playlist(secret.CATEGORY_A) print("Created playlist named", secret.CATEGORY_A) # If "Category B"playlist does not exist, create it if not category_b_lst: api.create_playlist(secret.CATEGORY_B) print("Created playlist named", secret.CATEGORY_B) print("Finding uncategorized songs...") playlists = api.get_all_user_playlist_contents()
for i in allcontents: if i['name'] == playlist_name: playlist_id = i['id'] if delete_old == True: print(('Deleting old playlist {0}'.format(playlist_name))) print(('Playlist ID: {0}'.format(playlist_id))) api.delete_playlist(playlist_id) else: exists = True existing_tracks = i['tracks'] if exists == False: print(('Creating playlist {0}'.format(playlist_name))) playlist_id = api.create_playlist(playlist_name, description=None, public=False) print(('Playlist ID: {0}'.format(playlist_id))) existing_tracks = [] if exists == False: print(('Creating playlist {0}'.format(playlist_name))) playlist_id = api.create_playlist(playlist_name, description=None, public=False) print(('Playlist ID: {0}'.format(playlist_id))) existing_tracks = [] existing_nids = [] existing_bands = defaultdict(int) for j in existing_tracks:
#[artist name, album name, song id, isthumbsup, tracks to ignore] slow = [['Artist 1', 'Album 1', 0, False, []], ['Artist 2', 'Album 2', 0, False, []], ['Artist 3', 'Album 3', 0, False, []]] fast = [['Artist 4', 'Album 4', 0, False, []], ['Artist 5', 'Album 5', 0, False, []], ['Artist 6', 'Album 6', 0, False, []]] #auth=Mobileclient.is_authenticated() if (logged_in) == True: print("Log In Successful") songsTooBig = api.get_all_songs(incremental=False, include_deleted=None, updated_after=None) #print(len(songs)) print("Song data recieved") fastId = api.create_playlist('dayFASTList', description=None, public=False) slowId = api.create_playlist('daySLOWList', description=None, public=False) songs = [] for song in songsTooBig: #print(song['rating']) yes = False try: if song['playCount'] < 7: yes = True else: yes = False except: song['playCount'] = 0 yes = True if yes: try:
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)
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)
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
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)
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