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)
Example #2
0
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")
Example #3
0
class GMusicAPI():
    def __init__(self, username=None, encrypted_pass=None):
        self._api = Mobileclient()
        self.logged_in = False
        if username and encrypted_pass:
            self.login(username, encrypted_pass)

    def login(self, username, encrypted_pass):
        self.logged_in = self._api.login(username, decrypt(encrypted_pass), Mobileclient.FROM_MAC_ADDRESS)

    def logout(self):
        self._api.logout()
        self.logged_in = False

    def clear_playlist(self, playlist_name):
        playlists = self._api.get_all_user_playlist_contents()
        playlist = [playlist for playlist in playlists if playlist['name'] == playlist_name][0]
        entry_ids = [entry['id'] for entry in playlist['tracks']]
        removed = self._api.remove_entries_from_playlist(entry_ids)
        return len(removed)

    def search(self, *args):
        """
        Returns the best-fitting track dict for the given information.
        :param args: Strings which can be artist, song title, album etc.
        :return:
        """
        query = sanitise_query(' '.join(args))

        result = self._api.search(query)

        song_results = result['song_hits']
        if not song_results:
            warnings.warn('Warning: query {} returned no song hits.'.format(query))
            return None

        tracks = [song_result['track'] for song_result in song_results[:5]]

        for track in tracks:
            if not is_tribute(track, query):
                return track

        warnings.warn('Warning: query {} returned no non-tribute song hits.'.format(query))
        return None

    def get_playlist_id(self, playlist_name):
        for playlist in self._api.get_all_playlists():
            if playlist['name'] == playlist_name:
                return playlist['id']
        raise ValueError("Playlist '{}' not found".format(playlist_name))

    def add_songs(self, playlist_name, tracks):
        playlist_id = self.get_playlist_id(playlist_name)
        track_ids = [track['nid'] for track in tracks if track]
        self._api.add_songs_to_playlist(playlist_id, track_ids)
Example #4
0
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)
Example #5
0
    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)
Example #6
0
class GMusicWS:
	def __init__(self, user, password, playlistName):
		self.playlistName = playlistName
		self.api = Mobileclient()
		print ("Logging into MobileClient API")
		self.api.login(user, password,"android_id") #insert unique android_id here

	def mapUnknownTracks(self, db):
		playlist = db.unmappedTracks()
		
		for track in playlist:
			searchstr = track.artist + " " + track.song
			print ("Searching for %s" % (searchstr))
			try:
				result = self.api.search_all_access(searchstr, max_results=1)
				print ("Found " + result['song_hits'][0]['track']['artist'] + " - " + result['song_hits'][0]['track']['title'])
				nid = result['song_hits'][0]['track']['nid']
				db.storemapping(track.song, track.artist, nid)
			except:
				print ("Error parsing result: " + str(result))
				
			time.sleep(1)
	
	def maintain(self, tracks):
		print ("Searching for playlist %s" % (self.playlistName))
				
		found = False
		searchres = self.api.get_all_playlists()
		for list in searchres:
			if list['name'] == self.playlistName:
				found = True
				pid = list['id']
				
		if not found:
			print ("Not found - creating")
			pid = self.api.create_playlist(self.playlistName)
		
		print ("Playlist id is %s" % (pid))
		
		print ("Getting current contents")
		playlists = self.api.get_all_user_playlist_contents()
		currentEntries = []
		for playlist in playlists:
			if playlist['name'] == self.playlistName:
				for entry in playlist['tracks']:
					currentEntries.append(entry['id'])

		print ("Removing songs")		
		self.api.remove_entries_from_playlist(currentEntries)
		
		print ("Adding songs")
		self.api.add_songs_to_playlist(pid, tracks)
def main():
    logger.info('Scanning the Billboard Charts......')
    chart_df_dict = {}
    global gmusic
    if not gmusic:
        gmusic = Mobileclient(debug_logging=False)
        gmusic.login(email=USER_NAME,
                     password=APP_PASSWORD,
                     locale='en_US',
                     android_id=Mobileclient.FROM_MAC_ADDRESS)
    for chart, chart_name in charts_to_playlist:
        logger.info('Getting {}......'.format(chart_name))
        chart_scraped = billboard.ChartData(config['BILLBOARD'][chart])
        chart_df = get_song_df(chart_scraped)
        #Delete Playlist if present.
        logger.info('Updating {} Playlist......'.format(chart_name))
        delete_playlist_if_exists(chart_name)
        #Create Playlist
        playlist_id = gmusic.create_playlist(
            name=chart_name,
            description=chart_name + ' from Billboard on week {}'.format(
                current_week(dt.datetime.today())),
            public=True)
        gmusic.add_songs_to_playlist(
            playlist_id=playlist_id,
            song_ids=chart_df.song_id.dropna().tolist())
        chart_df_dict[chart] = chart_df
        #Top Rising from Hot 100
        if chart_df_dict.has_key('HOT_100'):
            logger.info('Updating {} Playlist......'.format('Top 25 Risers'))
            top_100 = chart_df_dict['HOT_100']
            top_100.loc[top_100.change == 'Hot Shot Debut', 'change'] = 100
            top_100.change = pd.to_numeric(top_100.change,
                                           errors='coerce').fillna(0)
            hot25_risers = top_100.sort_values(
                by=['change'],
                ascending=False).head(25).sort_values(by=['rank'],
                                                      ascending=True)
            delete_playlist_if_exists('Top 25 Risers')
            playlist_id = gmusic.create_playlist(
                'Top 25 Risers',
                'Top 25 Risers from Hot 100 Billboard on week {}'.format(
                    current_week(dt.datetime.today())))
            gmusic.add_songs_to_playlist(
                playlist_id=playlist_id,
                song_ids=hot25_risers.song_id.dropna().tolist())
            logger.info('Finished Updating......'.format('Top 25 Risers'))
Example #8
0
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)
Example #9
0
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
Example #10
0
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)
Example #11
0
class GmSession:
    def __init__(self):
        self.session = Mobileclient()
        self.device_id = gmusic_device_id
        self.cred_path = gmusic_cred_path
        self.playlist_id = gmusic_playlist_id

    def login(self):
        self.session.oauth_login(device_id=self.device_id,
                                 oauth_credentials=self.cred_path)

    def logout(self):
        self.session.logout()

    def search(self, artist, song):
        search_string = f'{artist.lower()}' + f', {song.lower()}'
        results = self.session.search(search_string, max_results=20)
        if len(results['song_hits']) > 0:
            first_result = results['song_hits'][0]['track']
            if 'storeId' in first_result.keys():
                return first_result['storeId']
            elif 'id' in first_result.keys():
                print('bad id')
                return first_result['id']
            elif 'nid' in first_result.keys():
                print('bad id')
                return results['song_hits'][0]['track']['nid']
        else:
            print('No songs found...')

    def add_to_playlist(self, song_list):
        playlists = self.session.get_all_user_playlist_contents()
        for playlist in playlists:
            if playlist['id'] == self.playlist_id:
                to_remove = []
                for track in playlist['tracks']:
                    to_remove.append(track['id'])

                print('Adding new songs...')
                res = self.session.add_songs_to_playlist(
                    self.playlist_id, song_list)
                print('Removing previous songs...')
                out = self.session.remove_entries_from_playlist(to_remove)
                print('Finished')
Example #12
0
    if playlist["name"] == playlistName:
        if args.replace:
            logger.info("Deleting existing playlist")
            gpm.delete_playlist(playlist["id"])
        else:
            logger.error("Playlist already exists: " + playlistName)
            exit(1)

logger.info("Creating playlist: " + playlistName)
playlistId = gpm.create_playlist(playlistName, description="Autogenerated by RPG", public=False)

logger.info("Adding " + str(len(trackRows)) + " tracks to playlist")

trackIds = []
for row in trackRows:
    trackIds.append(row[0])

    if len(trackIds) >= 50:
        gpm.add_songs_to_playlist(playlist_id=playlistId, song_ids=trackIds)
        trackIds = []
        logger.debug("Added 50 tracks to playlist")
        time.sleep(10)


if len(trackIds) > 0:
    gpm.add_songs_to_playlist(playlist_id=playlistId, song_ids=trackIds)
    logger.debug("Added remaining " + str(len(trackIds)) + " tracks to playlist")

logger.info("DONE!")
db.close()
Example #13
0
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)
Example #14
0
            log.warning('Skipping playlist ' + playlist_name +
                        ' (no tracks available in All Access)')
            continue

        log.debug('Creating playlist ' + playlist_name)
        if not simulate:
            try:
                playlist_id = import_api.create_playlist(playlist_name)
            except CallFailure as e:
                log.error('Failed to create playlist ' + playlist_name)
                continue

        log.debug('Adding ' + str(len(track_ids)) + ' tracks to playlist')
        if not simulate:
            try:
                import_api.add_songs_to_playlist(playlist_id, track_ids)
            except CallFailure as e:
                log.error('Failed to add tracks to playlist ' + playlist_name)
                continue

# import all radio stations
if migration_type == 'all' or migration_type == 'stations':
    log.info('Importing ' + str(len(radio_stations)) + ' radio stations to ' +
             import_username)

    for station in radio_stations:
        station_name = station.get('name')
        station_id = get_station_id(station)

        if not station_id:
            log.warning(
Example #15
0
class FetchSongs(object):
    """
        Class to manage updating and creating of playlists and songs in the Google Play Music service for a specific
         Play Music account
    """
    def __init__(self):

        self.songs = []
        self.nids = []

        os.environ['REQUESTS_CA_BUNDLE'] = '/etc/ssl/certs/ca-certificates.crt'

        self.api = Mobileclient()
        self.api.login('xxxxxx', 'xxxxx', Mobileclient.FROM_MAC_ADDRESS)

    def add_songs_to_gmusic_playlist(self, playlist_id, song_ids):
        """ Add songs to the GMUSIC API playlist using song ids

        :param playlist_id: str playlist id
        :param song_ids: list of strings
        :returns: None
        """

        return self.api.add_songs_to_playlist(playlist_id, song_ids)

    def search_for_songs(self, artist, title):
        """ Search for songs in the GMUSIC API

        :param artist: string
        :param title: string
        :return song_nid: string
        """

        song_nid = None
        search_content = self.api.search(''.join([artist, ' ', title]))
        for each_song in search_content['song_hits']:
            song_detail = box.Box(each_song['track'])
            if artist.casefold() in song_detail.artist.casefold(
            ) and title.casefold() in song_detail.title.casefold():
                song_nid = song_detail.storeId
                break
        return song_nid

    def get_playlists_length(self, api_content):
        """ Get size of GMUSIC playlists in terms of number of songs

        :param api_content: dict
        :returns playlist_sizes: dict
        """

        playlist_sizes = {}
        api_playlists = self.api.get_all_playlists()
        for api_playlist in api_playlists:
            for content_playlist in api_content:
                if content_playlist['id'] == api_playlist['id']:
                    playlist_sizes[api_playlist['id']] = len(
                        content_playlist['tracks'])
        return playlist_sizes

    def get_available_playlists(playlist_dict):
        """ Get available playlist ids and associated number of songs
        :param playlist_dict: dict
        :return list_id, number_of_songs: tuple of list_id and number of songs
        """

        available_lists = {k for (k, v) in playlist_dict.items() if v <= 800}
        if available_lists:
            for list_id, number_of_songs in available_lists.items():
                yield (list_id, number_of_songs)
        else:
            return None

    @classmethod
    def create_gmusic_playlist(self, name):
        """ Create GMUSIC playlist
        :param name: string name of playlist
        :return: string success or fail"""
        return self.api.create_playlist(name)
def allaccessimport(playlist=None, username=None, password=None, dry_run=False):
    """
    Exports a Spotify playlist to stdout or csv.
    """

    if not username or not password:
        raise CommandError(
            "Username and password must be provided as either command-line " +
            "argument or in the application configuration file."
        )

    playlist_name = playlist
    playlist_description = ""
    if playlist:
        playlist_name = os.path.basename(playlist_name)
        playlist_name = os.path.splitext(playlist_name)[0]
    logging.debug("Playlist name will be: {}".format(playlist_name))

    api = Mobileclient(False, False)
    logged_in = api.login(username, password, Mobileclient.FROM_MAC_ADDRESS)
    if not logged_in:
        raise CommandError('Error. Unable to login to Google Music All Access.')

    playlist_ref = None
    currenttracks = None

    failed_tracks = list()
    songs_added = 0
    total = 0

    stream = open(playlist, "rb") if playlist else sys.stdin

    for input_line in stream:
        input_line = input_line.strip()

        # Lazily search the beginning of the file for a Playlist name
        if input_line.startswith("#"):
            data = input_line[1:]
            parts = [x.strip() for x in data.split(":", 1)]

            if len(parts) == 2:
                if parts[0] == "Playlist":
                    playlist_name = parts[1]
                elif parts[0] == "Description":
                    playlist_description = parts[1]

            continue

        if not playlist_ref:
            if not playlist_name:
                raise CommandError(
                    "A playlist name was not given and it was not found " +
                    "in the file either. Can't continue."
                )
            else:
                playlist_ref, currenttracks = get_playlist(api, playlist_name)
                if not playlist_ref and not dry_run:
                    sys.stderr.write('Playlist not found. Creating new.\n')
                    playlist_ref = api.create_playlist(playlist_name, description=playlist_description)
                yield 'Going to update playlist {0} ({1})\n'.format(
                    playlist_name, playlist_ref
                )

        trackinfo = list(csv.reader([input_line], quoting=csv.QUOTE_ALL))[0]

        if trackinfo[0] == 'Track' and trackinfo[1] == 'Artist':
            yield 'Skipping header.'
            continue

        search_term = "{0} {1}".format(trackinfo[0], trackinfo[1])
        total = total + 1
        newtrackid, error_reason = search_track(api, search_term, currenttracks)
        if newtrackid:
            if not dry_run:
                #print("Add to {} song {}".format(playlist_ref, newtrackid))
                api.add_songs_to_playlist(playlist_ref, newtrackid)
            songs_added = songs_added + 1
        else:
            failed_tracks.append(trackinfo)
        sys.stderr.write(
            "Searching {}...{}\n".format(search_term, error_reason)
        )


    yield "{0} songs added out of {1}. {2} Failed.".format(
        songs_added, total, total-songs_added
    )

    yield "Failed tracks:"
    for line in failed_tracks:
        print "  ", line
Example #17
0
song = {}
tids = []

for i,v in enumerate(playlist):
    tids.append(v['trackId'])

for i,v in enumerate(songs):
    if v['id'] not in tids:
	song = v

errSong = None
try:
	guid = song['id']
except:
	#library = api.get_all_songs()
	errSong = clusters[cluster][random.randint(0, len(clusters[cluster])-1)]
	guid = errSong['id']

#device = webapi.get_registered_devices()
#print device[0]['id'][2:]
#streamURL = api.get_stream_url(guid, device[0]['id'][2:])
streamURL = api.get_stream_url(guid, '320b3128904ea650')
if errSong is None:
    #pid = api.create_playlist("Tempo: " + dt.datetime.now().strftime("%m-%d-%Y %I:%M:%S%p"))
    api.add_songs_to_playlist(pid, guid)
    print json.dumps({"streamURL": streamURL, "song": song}, indent=4, separators=(',', ': '))
else:
    api.add_songs_to_playlist(pid, guid)
    print json.dumps({"streamURL": streamURL, "song": errSong, "error": "true"}, indent=4, separators=(',', ': '))
Example #18
0
class GMusicClient(ContentConsumer):
    '''Element in charge of interfacing with GMusicApi Client'''

    def __init__(self, data_cache):
        self.client = Mobileclient()
        self.data_cache = data_cache

    def login(self):
        '''Use data/unlocked/credentials.json to log in'''
        mac = Mobileclient.FROM_MAC_ADDRESS
        credentials = json.load(open('data/unlocked/credentials.json', 'r'))
        self.client.login(credentials['username'], credentials['password'], mac)

    def load_my_library(self):
        '''Load user's songs, playlists, and stations'''
        track_load = threading.Thread(target=self.load_tracks)
        radio_load = threading.Thread(target=self.load_radios)
        playlist_load = threading.Thread(target=self.load_playlists)

        track_load.start()
        radio_load.start()
        playlist_load.start()

        track_load.join()
        radio_load.join()
        playlist_load.join()

    def load_playlists(self):
        playlists = self.client.get_all_user_playlist_contents()
        playlists.reverse()
        self.data_cache.playlists = playlists

    def load_tracks(self):
        self.data_cache.tracks = [t for t in self.client.get_all_songs() if 'nid' in t]

    def scrape_song(self, track):
        return track

    def load_radios(self):
        radios = self.client.get_all_stations()
        radios.reverse()
        self.data_cache.radios = radios

    def get_radio_contents(self, radio_id):
        tracks = self.client.get_station_tracks(radio_id)
        return tracks

    def get_radio_list(self, name):
        return [r for r in self.data_cache.radios if name in r['name']]

    def filter(self, element, field, filter_by):
        return [e for e in element if filter_by in e[field]]

    def get_playlist_list(self, name):
        return self.filter(self.data_cache.playlists, 'name', name)

    def search_all_access(self, query):
        return self.client.search_all_access(query)

    def create_radio(self, seed_type, id, name):
        '''Create a radio'''
        # This is a weird way to do this, but there's no other choice
        ids = {"track": None, "album": None, "artist": None}
        seed_id_name = self.get_type_name(seed_type)
        ids[seed_id_name] = id
        return self.client.create_station(name=name, track_id=ids['track'], album_id=ids['album'], artist_id=ids['artist'])

    def search_items_all_access(self, type, query):
        '''Searches Albums, Artists, and Songs; uses metaprogramming'''
        index_arguments = self.get_index_arguments(type)

        items = self.search_all_access(query)['{0}_hits'.format(type[:-1])]
        return [self.format_item(item, type, index_arguments) for item in items]

    def get_sub_items(self, type_from, search_type, from_id):
        '''Here type_from refers to artist or album we're indexing against'''
        args = self.get_index_arguments(search_type)

        if type_from == 'playlist':
            return self.get_playlist_contents(from_id)

        else:
            # Get the appropriate search method and execute it
            search_method_name = 'get_{0}_info'.format(type_from)
            search_method = getattr(self.client, search_method_name)
            try:
                items = search_method(from_id, True)[args['type']+'s'] # True here includes subelements
            except:
                # User passed in a bad id or something
                return

        # Now return appropriately
        return [self.format_subitems(t, args) for t in items if args['id'] in t]

    def get_playlist_contents(self, from_id):
        '''Playlist exclusive stuff'''
        shareToken = [t for t in self.data_cache.playlists \
            if t['id'] == from_id][0]['shareToken']

        contents = self.client.get_shared_playlist_contents(shareToken)
        return [self.format_playlist_contents(t) for t in contents if 'track' in t]

    def get_suggested(self):
        '''Returns a list of tracks that the user might be interested in'''
        items = sorted(self.client.get_promoted_songs(), key=lambda y: y['title'])
        return [self.format_suggested(t) for t in items if 'storeId' in t]

    def get_information_about(self, from_type, from_id):
        '''Gets specific information about an id (depending on the type)'''
        if 'artist' in from_type.lower():
            return self.client.get_artist_info(from_id, include_albums=False)
        if 'album' in from_type.lower():
            return self.client.get_album_info(from_id, include_tracks=False)
        return self.client.get_track_info(from_id)

    def get_stream_url(self, nid):
        return self.client.get_stream_url(nid)

    def lookup(self, nid):
        return self.client.get_track_info(nid)

    def add_track_to_library(self, nid):
        self.client.add_aa_track(nid)

    def add_to_playlist(self, playlist_id, nid):
        self.client.add_songs_to_playlist(playlist_id, nid)

        playlist_load = threading.Thread(target=self.load_playlists)
        playlist_load.daemon = True
        playlist_load.start()

    def format_suggested(self, t):
        return (t['title'], t['storeId'], 'Play', t['album'])

    def format_playlist_contents(self, t):
        return (t['track']['title'], t['trackId'], 'Play', t['track']['album'])

    def format_subitems(self, t, args):
        return (t[args['name']], t[args['id']], args['command'], t[args['alt']])
class MusicSync(object):
    def __init__(self, email=None, password=None):
        self.mm = Musicmanager()
        self.wc = Webclient()
        self.mc = Mobileclient()
        if not email:
            email = raw_input("Email: ")
        if not password:
            password = getpass()

        self.email = email
        self.password = password

        self.logged_in = self.auth()

        print "Fetching playlists from Google..."
        self.playlists = self.mc.get_all_user_playlist_contents()
        #self.playlists = self.mc.get_all_playlists()
        #self.playlists = self.wc.get_all_playlist_ids(auto=False)
        self.all_songs = self.mc.get_all_songs()
        #print "Got %d playlists." % len(self.playlists['user'])
        print "Got %d playlists containing %d songs." % (len(self.playlists), len(self.all_songs))
        print ""


    def auth(self):
        self.logged_in = self.mc.login(self.email, self.password)
        #self.logged_in = self.wc.login(self.email, self.password)
        if not self.logged_in:
            print "Login failed..."
            exit()

        print ""
        print "Logged in as %s" % self.email
        print ""

        if not os.path.isfile(OAUTH_FILEPATH):
            print "First time login. Please follow the instructions below:"
            self.mm.perform_oauth()
        self.logged_in = self.mm.login()
        if not self.logged_in:
            print "OAuth failed... try deleting your %s file and trying again." % OAUTH_FILEPATH
            exit()

        print "Authenticated"
        print ""


    def sync_playlist(self, filename, remove_missing):
    #def sync_playlist(self, filename, remove_missing=False):
        filename = self.get_platform_path(filename)
        os.chdir(os.path.dirname(filename))
        title = os.path.splitext(os.path.basename(filename))[0]
        print "Syncing playlist: %s" % filename
        #if title not in self.playlists['user']:
            #print "   didn't exist... creating..."
            #self.playlists['user'][title] = [self.wc.create_playlist(title)]
        print ""

        plid = ""

        for pl in self.playlists:
            if pl['name'] == title:
                plid = pl['id']
                goog_songs = pl['tracks']

        if plid == "":
            print "   didn't exist... creating..."
            plid = self.mc.create_playlist(self, title)

        #plid = self.playlists['user'][title][0]
        #goog_songs = self.wc.get_playlist_songs(plid)
        print "%d songs already in Google Music playlist" % len(goog_songs)
        pc_songs = self.get_files_from_playlist(filename)
        print "%d songs in local playlist" % len(pc_songs)
        print ""

        # Sanity check max 1000 songs per playlist
        if len(pc_songs) > MAX_SONGS_IN_PLAYLIST:
            print "    Google music doesn't allow more than %d songs in a playlist..." % MAX_SONGS_IN_PLAYLIST
            print "    Will only attempt to sync the first %d songs." % MAX_SONGS_IN_PLAYLIST
            del pc_songs[MAX_SONGS_IN_PLAYLIST:]

        existing_files = 0
        added_files = 0
        failed_files = 0
        removed_files = 0
        fatal_count = 0

        for fn in pc_songs:
            if self.file_already_in_list(fn, goog_songs, self.all_songs):
                existing_files += 1
                continue
            print ""
            print "Adding: %s" % os.path.basename(fn).encode('cp1252')
            #print "Adding: %s" % os.path.basename(fn)
            #online = False
            online = self.find_song(fn, goog_songs, self.all_songs)
            #online = self.find_song(fn)
            song_id = None
            if online:
                song_id = online['id']
                print "   already uploaded [%s]" % song_id
            else:
                attempts = 0
                result = []
                while not result and attempts < MAX_UPLOAD_ATTEMPTS_PER_FILE:
                    print "   uploading... (may take a while)"
                    attempts += 1
                    try:
                        result = self.mm.upload(fn)
                    except (BadStatusLine, CannotSendRequest):
                        # Bail out if we're getting too many disconnects
                        if fatal_count >= MAX_CONNECTION_ERRORS_BEFORE_QUIT:
                            print ""
                            print "Too many disconnections - quitting. Please try running the script again."
                            print ""
                            exit()

                        print "Connection Error -- Reattempting login"
                        fatal_count += 1
                        self.wc.logout()
                        self.mc.logout()
                        self.mm.logout()
                        result = []
                        time.sleep(STANDARD_SLEEP)

                    except:
                        result = []
                        time.sleep(STANDARD_SLEEP)

                try:
                    if result[0]:
                        song_id = result[0].itervalues().next()
                    else:
                        song_id = result[1].itervalues().next()
                    print "   upload complete [%s]" % song_id
                except:
                    print "      upload failed - skipping"
                    tag = self.get_id3_tag(fn)
                    print "      failed song:\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252'))

            if not song_id:
                failed_files += 1
                continue

            added = self.mc.add_songs_to_playlist(plid, song_id)
            time.sleep(.3) # Don't spam the server too fast...
            print "   done adding to playlist"
            added_files += 1

        if remove_missing:
            for g in goog_songs:
                for s in self.all_songs:
                    if g['trackId'] == s['id']:
                        print ""
                        print "Removing: %s" % s['title'].encode('cp1252')
                        self.mc.remove_entries_from_playlist(g['id'])
                        #self.wc.remove_songs_from_playlist(plid, s.id)
                        time.sleep(.3) # Don't spam the server too fast...
                        removed_files += 1

        print ""
        print "---"
        print "%d songs unmodified" % existing_files
        print "%d songs added" % added_files
        print "%d songs failed" % failed_files
        print "%d songs removed" % removed_files


    def get_files_from_playlist(self, filename):
        files = []
        f = codecs.open(filename, encoding='cp1252')
        #f = codecs.open(filename, encoding='utf-8')
        for line in f:
            line = line.rstrip().replace(u'\ufeff',u'')
            if line == "" or line[0] == "#":
                continue
            path  = os.path.abspath(self.get_platform_path(line))
            if not os.path.exists(path):
                print "File not found: %s" % line
                continue
            files.append(path)
        f.close()
        return files

    def file_already_in_list(self, filename, goog_songs, all_songs):
        tag = self.get_id3_tag(filename)
        print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252'))
        i = 0
        while i < len(goog_songs):
            for s in all_songs:
                if goog_songs[i]['trackId'] == s['id']:
                    if self.tag_compare(s, tag):
                        print "Found match\t%s\t%s\t%s" % (s['title'].encode('cp1252'), s['artist'].encode('cp1252'), s['album'].encode('cp1252'))
                        goog_songs.pop(i)
                        return True
            i += 1
        return False

    def get_id3_tag(self, filename):
        data = mutagen.File(filename, easy=True)
        r = {}
        if 'title' not in data:
            title = os.path.splitext(os.path.basename(filename))[0]
            print 'Found song with no ID3 title, setting using filename:'
            print '  %s' % title
            print '  (please note - the id3 format used (v2.4) is invisible to windows)'
            data['title'] = [title]
            data.save()
        r['title'] = data['title'][0]
        r['track'] = int(data['tracknumber'][0].split('/')[0]) if 'tracknumber' in data else 0
        # If there is no track, try and get a track number off the front of the file... since thats
        # what google seems to do...
        # Not sure how google expects it to be formatted, for now this is a best guess
        if r['track'] == 0:
            m = re.match("(\d+) ", os.path.basename(filename))
            if m:
                r['track'] = int(m.group(0))
        r['artist'] = data['artist'][0] if 'artist' in data else ''
        r['album'] = data['album'][0] if 'album' in data else ''
        return r

    def find_song(self, filename, goog_songs, all_songs):
        tag = self.get_id3_tag(filename)
        print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'), tag['artist'].encode('cp1252'), tag['album'].encode('cp1252'))
        #results = self.wc.search(tag['title'])
        # NOTE - diagnostic print here to check results if you're creating duplicates
        #print results['song_hits']
        #for r in goog_songs:
        #for r in results['song_hits']:
        for s in all_songs:
            #if r['trackId'] == s['id']:
            if self.tag_compare(s, tag):
                # TODO: add rough time check to make sure its "close"
                print "Found match\t%s\t%s\t%s" % (s['title'].encode('cp1252'), s['artist'].encode('cp1252'), s['album'].encode('cp1252'))
                return s
        return None

    def tag_compare(self, g_song, tag):
        # If a google result has no track, google doesn't return a field for it
        if 'title' not in g_song:
            g_song['title'] = ""
        if 'artist' not in g_song:
            g_song['artist'] = ""
        if 'album' not in g_song:
            g_song['album'] = ""
        if 'track' not in g_song:
            g_song['track'] = 0
        if (g_song['title'].lower() == tag['title'].lower() and g_song['artist'].lower() == tag['artist'].lower()) or\
           (g_song['album'].lower() == tag['album'].lower() and g_song['title'].lower() == tag['title'].lower()) or\
           (g_song['artist'].lower() == tag['artist'].lower() and g_song['album'].lower() == tag['album'].lower() and g_song['track'] == tag['track']):
            print "Partial match\t%s\t%s\t%s" % (g_song['title'].encode('cp1252'), g_song['artist'].encode('cp1252'), g_song['album'].encode('cp1252'))
        return g_song['title'].lower() == tag['title'].lower() and\
               g_song['artist'].lower() == tag['artist'].lower() and\
               g_song['album'].lower() == tag['album'].lower() #and\
               #g_song['track'] == tag['track']

    def delete_song(self, sid):
        self.mc.delete_songs(sid)
        print "Deleted song by id [%s]" % sid

    def get_platform_path(self, full_path):
        # Try to avoid messing with the path if possible
        if os.sep == '/' and '\\' not in full_path:
            return full_path
        if os.sep == '\\' and '\\' in full_path:
            return full_path
        if '\\' not in full_path:
            return full_path
        return os.path.normpath(full_path.replace('\\', '/'))
        if token:
            sp = spotipy.Spotify(auth=token)
            playlists = sp.user_playlists(username)
            for playlist in playlists['items']:
                if playlist['owner']['id'] == username and playlist['name'] not in current_playlists:
                    print
                    playlist_name = playlist['name']
                    print "Creating playlist: " + playlist_name

                    # Create new Google Play Music playlist with the same name as the Spotify playlist
                    playlist_id = mc.create_playlist(playlist_name)

                    # Extract the tracks from the Spotify playlist
                    results = sp.user_playlist(username, playlist['id'], fields="tracks,next")
                    tracks = results['tracks']

                    # Obtain the Google Play Music track IDs and add them to the new playlist
                    track_ids = find_tracks(tracks, mc)
                    mc.add_songs_to_playlist(playlist_id, track_ids)

                    # Loop for playlists that are longer than 100 tracks
                    while tracks['next']:
                        tracks = sp.next(tracks)
                        track_ids = find_tracks(tracks, mc)
                        print track_ids
                        mc.add_songs_to_playlist(playlist_id, track_ids)
        else:
            print "Cannot get valid token for Spotify"
    else:
        print "Cannot login to Google Play Music"
Example #21
0
    print "usage:" + sys.argv[0] + " filename [playlist name]"
    sys.exit()

file = params[1]

if len(params) == 3:
    plname = params[2]
else:
    plname = None

mm = Musicmanager()
api = Mobileclient()
mm.login()
api.login('GoogleID', 'Password')

track = mm.upload(file)
track_id = track[0][file]

if plname:
    playlist_id = None
    playlists = api.get_all_playlists()
    for playlist in playlists:
        if plname == playlist['name']:
            playlist_id = playlist['id']
            break

    if playlist_id == None:
        playlist_id = api.create_playlist(plname)

    api.add_songs_to_playlist(playlist_id, track_id)
class PlaylistManager(object):
    def __init__(self, email_file):
        self.login(email_file)
        self._get_songs()
        self._get_playlists()

    def login(self, email_file):
        with open(os.path.expanduser(email_file), 'r') as gmail_email_file:
            gmail_email = gmail_email_file.read()
        print "Password for " + gmail_email.strip('\n') + ":"
        pw = getpass.getpass()
        self.client = Mobileclient()
        self.client.login(gmail_email, pw)

    def _get_songs(self):
        self.all_songs = self.client.get_all_songs()
        assert isinstance(self.all_songs, list)
        self.all_songs_by_id = {}
        for song in self.all_songs:
            self.all_songs_by_id[song['id']] = song

    def _playlist_entry_dict(self, entry_dict, playlist_name):
        track_dict = self.all_songs_by_id[entry_dict['trackId']]
        track_hash = hash(playlist_name + track_dict['title'] + track_dict['artist'] + track_dict['album'])
        return {'title': track_dict['title'], 'entry_id': entry_dict['id'], 'track_id': entry_dict['trackId'],
                'hash': track_hash}

    def _get_playlists(self):
        self.all_playlists = self.client.get_all_user_playlist_contents()
        assert isinstance(self.all_playlists, list)


    def sort_and_deduplicate_playlists(self):
        """de-duplicates and then sorts all user playlists"""
        playlist_entry_ids_to_be_removed = []
        for playlist in self.all_playlists:
            current_playlist = []
            current_playlist_entry_hashes = []
            for track in playlist['tracks']:
                if not track['deleted']:
                    current_track = self._playlist_entry_dict(track, playlist['name'])
                    if not current_track['hash'] in current_playlist_entry_hashes:
                        current_playlist.append(current_track)
                        current_playlist_entry_hashes.append(current_track['hash'])
                    else:
                        # we will remove these duplicates later, so they don't mess up our sorting
                        playlist_entry_ids_to_be_removed.append(current_track['entry_id'])
            if playlist_entry_ids_to_be_removed != []:
                print "De-duplicated " + playlist['name']
            get_track_title = lambda track_dict: track_dict['title']
            sorted_songs = sorted(current_playlist, key=get_track_title)
            sorted_track_ids = []
            sorted_entry_ids = []
            for track in sorted_songs:
                sorted_track_ids.append(track['track_id'])
                sorted_entry_ids.append(track['entry_id'])
            # if the playlist is already sorted, don't bother sorting it
            if sorted_songs != current_playlist:
                # remove all things at once for maximum efficiency
                self.client.add_songs_to_playlist(playlist['id'], sorted_track_ids)
                playlist_entry_ids_to_be_removed += sorted_entry_ids
                print "Re-ordered " + playlist['name']
        if playlist_entry_ids_to_be_removed != []:
            # remove all things to be removed at once
            self.client.remove_entries_from_playlist(playlist_entry_ids_to_be_removed)

    def export_songs(self, csv_output):
        """writes a csv file containing info on all the songs"""
        # these are the field names. More might exist, but I haven't found them
        field_names = [u'comment', u'rating', u'composer', u'year', u'creationTimestamp', u'id', u'album',
                       u'totalDiscCount', u'title', u'recentTimestamp', u'albumArtist', u'trackNumber', u'discNumber',
                       u'deleted', u'totalTrackCount', u'estimatedSize', u'beatsPerMinute', u'genre', u'playCount',
                       u'kind', u'artist', u'lastModifiedTimestamp', u'clientId', u'durationMillis', u'albumArtRef',
                       u'artistId', u'storeId', u'nid', u'albumId', u'artistArtRef', u'trackType', u'trackOrigin',
                       u'contentType', u'lastRatingChangeTimestamp']
        with open(csv_output, 'w') as output_file_obj:
            csv_writer = csv.DictWriter(output_file_obj, field_names)
            csv_writer.writeheader()
            for song in self.all_songs:
                for x in song:
                    # remove the unicode, replacing bad characters with "?"
                    if isinstance(song[x], unicode):
                        song[x] = song[x].encode('ascii', 'replace')
                csv_writer.writerow(song)

    def export_playlists(self, output_path):
        """writes the csv contents of each playlist to the playlist-named file
        in the specified folder"""
        if not os.path.exists(output_path):
            os.mkdir(output_path)
        if not os.path.isdir(output_path):
            raise Exception("Not a folder!")
        playlist_entry_keys = [u'absolutePosition', u'clientId', u'creationTimestamp', u'deleted', u'id', u'kind',
                               u'lastModifiedTimestamp', u'playlistId', u'source', u'trackId']
        for playlist in self.all_playlists:
            file_name = os.path.join(output_path, playlist[u'name'] + '.csv')
            with open(file_name, 'w') as output_file:
                csv_writer = csv.DictWriter(output_file, playlist_entry_keys)
                csv_writer.writeheader()
                for track in playlist[u'tracks']:
                    for x in track:
                        # remove the unicode, replacing bad characters with "?"
                        if isinstance(track[x], unicode):
                            track[x] = track[x].encode('ascii', 'replace')
                    csv_writer.writerow(track)


    def make_local_xspf_playlists(self):
        """write playlists in xspf format to named files in the playlist folder.
        Credit to https://github.com/alastair/xspf for the wonderful xspf python module"""
        # output_path = 'playlists'
        output_path = os.path.expanduser('~/Music/Playlists')
        music_path = os.path.expanduser('~/Music')
        extension = '.mp3'
        if not os.path.exists(output_path):
            os.mkdir(output_path)
        if not os.path.isdir(output_path):
            raise Exception("Not a folder!")
        for playlist in self.all_playlists:
            output_file_path = os.path.join(output_path, playlist[u'name'] + '.xspf')
            playlist_xspf = xspf.Xspf()
            playlist_xspf.title = playlist[u'name']
            for track in playlist['tracks']:
                track_xspf = xspf.Track()
                track_id = track['trackId']
                track_dict = self.all_songs_by_id[track_id]
                artist = track_dict['artist']
                album = track_dict['album']
                title = track_dict['title']
                track_xspf.title = title
                track_xspf.album = album
                track_xspf.creator = artist
                # replace is to fix the slash in 'AC/DC'
                track_path = 'file://' + os.path.join(music_path, artist.replace('/', ','), album.replace('/', ','),
                                                      title.replace('/', ',') + extension)
                track_xspf.location = track_path
                length = str(int(track_dict['durationMillis']) / 1000)
                track_xspf.duration = length
                playlist_xspf.add_track(track_xspf)
            with open(output_file_path, 'wb') as output_file:
                # we write this header so that Sublime Text recognizes it as an XML file, and sets syntax accordingly
                output_file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
                output_file.write(playlist_xspf.toXml(pretty_print=True))
Example #23
0
    count = min(len(table), args.li)
else:
    count = len(table)

if logged_in:
    playLists = api.get_all_user_playlist_contents()
    playlist = next(p for p in playLists if p['name'] == args.pl)

    for i in range(0, count):
        column = table[i].xpath('td')

        """SomaFM prints breaks and in the schedule so we only want rows in the
            table that are a track"""
        if len(column) == 5:
            song.artist = column[1].xpath('a')[0].text
            song.songName = column[2].text
            song.album = column[3].xpath('a')[0].text

            result = api.search_all_access(song.artist + " " + song.songName, 1)

            if result['song_hits']:
                songInPlaylist = False
                for track in playlist['tracks']:
                    if track['trackId'] == result['song_hits'][0]['track']['nid']:
                        print 'playlist already contains ', song.songName
                        songInPlaylist = True

                if not songInPlaylist:
                    print 'playlist dose not already contain ', song.songName
                    api.add_songs_to_playlist(playlist['id'],
                                              result['song_hits'][0]['track']['nid'])
        if len(track_ids) == 0:
            log.warning('Skipping playlist ' + playlist_name + ' (no tracks available in All Access)')
            continue
            
        log.debug('Creating playlist ' + playlist_name)
        if not simulate:
            try:
                playlist_id = import_api.create_playlist(playlist_name)
            except CallFailure as e:
                log.error('Failed to create playlist ' + playlist_name)
                continue

        log.debug('Adding ' + str(len(track_ids)) + ' tracks to playlist')
        if not simulate:
            try:
                import_api.add_songs_to_playlist(playlist_id, track_ids)
            except CallFailure as e:
                log.error('Failed to add tracks to playlist ' + playlist_name)
                continue

# import all radio stations
if migration_type == 'all' or migration_type == 'stations':
    log.info('Importing ' + str(len(radio_stations)) + ' radio stations to ' + import_username)
    
    for station in radio_stations:
        station_name = station.get('name')
        station_id = get_station_id(station)

        if not station_id:
            log.warning('Skipping station with invalid ID (probably not All Access)')
            continue
Example #25
0
class GMusic(object):
    def __init__(self):
        self.authenticated = False
        self.all_access = False
        self.library_loaded = False
        self.all_songs = []
        self.letters = {}
        self.artists = {}
        self.albums = {}
        self.genres = {}
        self.tracks_by_letter = {}
        self.tracks_by_artist = {}
        self.tracks_by_album = {}
        self.tracks_by_genre = {}
        self._device = None
        self._webclient = Webclient(debug_logging=False)
        self._mobileclient = Mobileclient(debug_logging=False)
        self._playlists = []
        self._playlist_contents = []
        self._stations = []

    def _get_device_id(self):
        if self.authenticated:
            devices = self._webclient.get_registered_devices()
            for dev in devices:
                if dev['type'] == 'PHONE':
                    self._device = dev['id'][2:]
                    break
                elif dev['type'] == 'IOS':
                    self._device = dev['id']
                    break

    def _set_all_access(self):
        settings = self._webclient._make_call(webclient.GetSettings, '')
        self.all_access = True if 'isSubscription' in settings['settings'] and settings['settings']['isSubscription'] == True else False

    def _set_all_songs(self):
        if len(self.all_songs) == 0:
            try:
                self.all_songs = self._mobileclient.get_all_songs()
            except NotLoggedIn:
                if self.authenticate():
                    self.all_songs = self._mobileclient.get_all_songs()
                else:
                    return []

        else:
            return self.all_songs

    def authenticate(self, email, password):
        try:
            mcauthenticated = self._mobileclient.login(email, password)
        except AlreadyLoggedIn:
            mcauthenticated = True

        try:
            wcauthenticated = self._webclient.login(email, password)
        except AlreadyLoggedIn:
            wcauthenticated = True

        self.authenticated = mcauthenticated and wcauthenticated
        self._set_all_access()
        self._get_device_id()
        return self.authenticated

    def load_data(self):
        self._set_all_songs()
        for song in self.all_songs:
            thumb = None
            letter = song['title'][0]
            artist = song['artist']
            album = song['album']
            genre = song['genre'] if 'genre' in song else '(None)'

            if letter not in self.tracks_by_letter:
                self.tracks_by_letter[letter] = []
                self.letters[letter] = None

            if artist not in self.tracks_by_artist:
                self.tracks_by_artist[artist] = []
                self.artists[artist] = None

            if album not in self.tracks_by_album:
                self.tracks_by_album[album] = []
                self.albums[album] = None

            if genre not in self.tracks_by_genre:
                self.tracks_by_genre[genre] = []
                self.genres[genre] = None

            track = {'artist': artist, 'album': album}

            if 'title' in song:
                track['title'] = song['title']

            if 'album' in song:
                track['album'] = song['album']

            if 'artist' in song:
                track['artist'] = song['artist']

            if 'durationMillis' in song:
                track['durationMillis'] = song['durationMillis']

            if 'id' in song:
                track['id'] = song['id']

            if 'trackNumber' in song:
                track['trackType'] = song['trackNumber']

            if 'storeId' in song:
                track['storeId'] = song['storeId']

            if 'albumArtRef' in song:
                track['albumArtRef'] = song['albumArtRef']
                thumb = song['albumArtRef'][0]['url']
                self.letters[letter] = thumb
                self.artists[artist] = thumb
                self.albums[album] = thumb
                self.genres[genre] = thumb

            self.tracks_by_letter[letter].append({'track': track, 'thumb': thumb, 'id': song['id']})
            self.tracks_by_artist[artist].append({'track': track, 'thumb': thumb, 'id': song['id']})
            self.tracks_by_album[album].append({'track': track, 'thumb': thumb, 'id': song['id']})
            self.tracks_by_genre[genre].append({'track': track, 'thumb': thumb, 'id': song['id']})

        self.library_loaded = True

    def get_tracks_for_type(self, type, name):
        type = type.lower()
        if type == 'artists':
            return self.tracks_by_artist[name]
        elif type == 'albums':
            return self.tracks_by_album[name]
        elif type == 'genres':
            return self.tracks_by_genre[name]
        elif type == 'songs by letter':
            return self.tracks_by_letter[name]
        else:
            return {}

    def get_song(self, id):
        return [x for x in self.all_songs if x['id'] == id][0]

    def get_all_playlists(self):
        if len(self._playlists) == 0:
            try:
                self._playlists = self._mobileclient.get_all_playlists()
            except NotLoggedIn:
                if self.authenticate():
                    self._playlists = self._mobileclient.get_all_playlists()
                else:
                    return []

        return self._playlists

    def get_all_user_playlist_contents(self, id):
        tracks = []
        if len(self._playlist_contents) == 0:
            try:
                self._playlist_contents = self._mobileclient.get_all_user_playlist_contents()
            except NotLoggedIn:
                if self.authenticate():
                    self._playlist_contents = self._mobileclient.get_all_user_playlist_contents()
                else:
                    return []

        for playlist in self._playlist_contents:
            if id == playlist['id']:
                tracks = playlist['tracks']
                break

        return tracks

    def get_shared_playlist_contents(self, token):
        playlist = []
        try:
            playlist = self._mobileclient.get_shared_playlist_contents(token)
        except NotLoggedIn:
            if self.authenticate():
                playlist = self._mobileclient.get_shared_playlist_contents(token)
            else:
                return []

        return playlist

    def get_all_stations(self):
        if len(self._stations) == 0:
            try:
                self._stations = self._mobileclient.get_all_stations()
            except NotLoggedIn:
                if self.authenticate():
                    self._stations = self._mobileclient.get_all_stations()
                else:
                    return []

        return self._stations

    def get_station_tracks(self, id, num_tracks=200):
        tracks = []
        try:
            tracks = self._mobileclient.get_station_tracks(id, num_tracks)
        except NotLoggedIn:
            if self.authenticate():
                tracks = self._mobileclient.get_station_tracks(id, num_tracks)
            else:
                return []

        return tracks

    def get_genres(self):
        genres = []
        try:
            genres = self._mobileclient.get_genres()
        except NotLoggedIn:
            if self.authenticate():
                genres = self._mobileclient.get_genres()
            else:
                return []

        return genres

    def create_station(self, name, id):
        station = None
        try:
            station = self._mobileclient.create_station(name=name, genre_id=id)
        except NotLoggedIn:
            if self.authenticate():
                station = self._mobileclient.create_station(name=name, genre_id=id)
            else:
                return []

        return station

    def search_all_access(self, query, max_results=50):
        results = None
        try:
            results = self._mobileclient.search_all_access(query, max_results)
        except NotLoggedIn:
            if self.authenticate():
                results = self._mobileclient.search_all_access(query, max_results)
            else:
                return []

        return results

    def get_artist_info(self, id, include_albums=True, max_top_tracks=5, max_rel_artist=5):
        results = None
        try:
            results = self._mobileclient.get_artist_info(id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist)
        except NotLoggedIn:
            if self.authenticate():
                results = self._mobileclient.get_artist_info(id, include_albums=include_albums, max_top_tracks=max_top_tracks, max_rel_artist=max_rel_artist)
            else:
                return []

        return results

    def get_album_info(self, id, include_tracks=True):
        results = None
        try:
            results = self._mobileclient.get_album_info(id, include_tracks=include_tracks)
        except NotLoggedIn:
            if self.authenticate():
                results = self._mobileclient.get_album_info(id, include_tracks=include_tracks)
            else:
                return []

        return results

    def add_aa_track(self, id):
        track = None
        try:
            track = self._mobileclient.add_aa_track(id)
        except NotLoggedIn:
            if self.authenticate():
                track = self._mobileclient.add_aa_track(id)
            else:
                return None

        return track

    def add_songs_to_playlist(self, playlist_id, song_ids):
        tracks = None
        try:
            tracks = self._mobileclient.add_songs_to_playlist(playlist_id, song_ids)
        except NotLoggedIn:
            if self.authenticate():
                tracks = self._mobileclient.add_songs_to_playlist(playlist_id, song_ids)
            else:
                return None

        return tracks

    def get_stream_url(self, id):
        try:
            stream_url = self._mobileclient.get_stream_url(id, self._device)
        except NotLoggedIn:
            if self.authenticate():
                stream_url = self._mobileclient.get_stream_url(id, self._device)
            else:
                return ''
        except CallFailure:
            raise CallFailure('Could not play song with id: ' + id, 'get_stream_url')

        return stream_url

    def reset_library(self):
        self.library_loaded = False
        self.all_songs[:] = []
        self._playlists[:] = []
        self._playlist_contents[:] = []
        self._stations[:] = []
        self.letters.clear()
        self.artists.clear()
        self.albums.clear()
        self.genres.clear()
        self.tracks_by_letter.clear()
        self.tracks_by_artist.clear()
        self.tracks_by_album.clear()
        self.tracks_by_genre.clear()
print "Available genres:"
for g in genres_map.keys():
    if len(g) == 0:
        continue  # empty genre tag

    if len(genres_map[g]) >= 20:
        print g + "\t" + str(len(genres_map[g])) + " tracks --> generating playlist"
        good_genres.append(g)

    else:
        print g + "\t" + str(len(genres_map[g])) + " tracks --> too few tracks"

print

# remove all existing auto-playlists first
playlists = api.get_all_playlists()
for p in playlists:
    if p["name"].startswith("genre auto-playlist"):
        print "Removing playlist " + p["name"]
        api.delete_playlist(p["id"])

for genre in good_genres:
    playlist = genres_map[genre]
    playlist_name = "genre auto-playlist - " + genre
    print "Creating playlist " + playlist_name

    pid = api.create_playlist(
        playlist_name, description="Test " + genre + " playlist created automatically with a script", public=False
    )
    api.add_songs_to_playlist(pid, playlist)
Example #27
0
def main():
    log.setLevel(logging.INFO)
    logging.getLogger('gmusicapi').setLevel(logging.INFO)
    
    cred_path = os.path.join(os.path.expanduser('~'), '.gmusic-sync')

    if not os.path.isfile(cred_path):
        raise NoCredentialException(
                    'No username/password was specified. No config file could '
                    'be found either. Try creating %s and specifying your '
                    'username/password there. Make sure to chmod 600.'
                    % cred_path)
    if not oct(os.stat(cred_path)[os.path.stat.ST_MODE]).endswith('00'):
        raise NoCredentialException(
                    'Config file is not protected. Please run: '
                    'chmod 600 %s' % cred_path)
    
    config = ConfigParser.ConfigParser()
    config.read(cred_path)

    src_user = config.get('src','username')
    src_pass = config.get('src','password')
    src_device = config.get('src','deviceid')

    dst_user = config.get('dst','username')
    dst_pass = config.get('dst','password')
    dst_device = config.get('dst','deviceid')

    if not src_user or not src_pass or not dst_user or not dst_pass:
        raise NoCredentialException(
                    'No username/password could be read from config file'
                    ': %s' % cred_path)
    if not src_device or not dst_device:
         raise NoCredentialException(
                    'No deviceId could be read from config file'
                    ': %s' % cred_path)

    parser = argparse.ArgumentParser(description='gmusic-sync', add_help=False)

    parser.add_argument('-hs', '--strict-heuristics', help='Songs must match artist, album, and title to be considered a match.', action='store_true', dest='strict_heuristics')
    parser.add_argument('-l', '--list', help='List playlists on the src account', action='store_true', dest='lst')
    parser.add_argument('-p', '--playlist', help='Playlist ID from src account to transfer', dest='playlist')
    args = parser.parse_args()

    api = Mobileclient()
    api.login(src_user, src_pass, src_device)

    playlists = api.get_all_playlists()

    if args.lst:
        for playlist in playlists:
            print playlist['name'] + ' (' + playlist['id'] + ') '
        exit()

    library = api.get_all_songs()

    api2 = Mobileclient()
    api2.login(dst_user, dst_pass, dst_device)
    library2 = api2.get_all_songs()

    if args.playlist is None:
        print 'Error: no playlist selected'

    all_playlist_entries = api.get_all_user_playlist_contents()

    selected_playlist_entries = []
    dst_playlist_id = None
    
    for entry in all_playlist_entries:
        if entry['id'] == args.playlist:
            selected_playlist_entries = entry['tracks']
            dst_playlist_id = api2.create_playlist(entry['name'])

    if dst_playlist_id is None:
        print 'Error creating new playlist'
        exit()

    playlist_tracks = []

    for ptrack in selected_playlist_entries:
        track_found = False
        for track in library:
            if ptrack['trackId'] == track['id']:
                playlist_tracks.append(track)
                track_found = True
                break
            try:
                if ptrack['trackId'] == track['storeId']:
                    playlist_tracks.append(track)
                    track_found = True
                    break
            except:
                pass
        if not track_found:
            print 'ERROR: could not find playlist entry ' + str(ptrack)
            api.add_aa_track(ptrack['trackId'])

    if len(playlist_tracks) != len(selected_playlist_entries):
        print 'Error: could not locate all playlist tracks in src library'
        exit()
    
    failed_tracks = []

    for track in playlist_tracks:
        try:
            if track['storeId'].startswith('T'):
                #It's a store track: does it exist in the target store?
                #Perform a store lookup: this will raise an exception if the track
                #Is not in the target store
                store_track = api2.get_track_info(track['storeId'])
                #If we got here, we're good to go for adding the track to the playlist
                retval = api2.add_songs_to_playlist(dst_playlist_id, track['storeId'])
                if track['storeId'] not in retval:
                    print 'Error adding   '  + track['title'] + ' - ' + track['artist'] + ' (' + track['album'] + ')'
            else:
                dst_track = heuristic_search(library2, track, args.strict_heuristics)
                if dst_track is not None:
                    api2.add_songs_to_playlist(dst_playlist_id, dst_track['id'])
                else:
                    failed_tracks.append(track)
        except:
            #Not a store track: do heuristics lookup
            dst_track = heuristic_search(library2, track, args.strict_heuristics)
            if dst_track is not None:
                api2.add_songs_to_playlist(dst_playlist_id, dst_track['id'])
            else:
                failed_tracks.append(track)

            continue

    print '----------------- FAILED TRACKS --------------------'
    for track in failed_tracks:
        print track['title'] + ' - ' + track['artist'] + ' (' + track['album'] + ')'
	song_hits = result['song_hits']

	print 'Found {} song(s).'.format(len(song_hits))

	if len(song_hits) == 0:
		not_found_songs.append(song)
	else:
		song_hit = song_hits[0] # Only add first search result for this song to playlist
		track = song_hit['track']

		msg = u'Adding "{t[artist]} - {t[title]}"'.format(t=track)
		if 'album' in track.keys():
			msg = msg + u' from album "{t[album]}"'.format(t=track)
		if 'year' in track.keys():
			msg = msg + u' ({t[year]})'.format(t=track)
		print msg
		print

		storeIds.append(track['storeId'])

# Create Google Play Music playlist and insert songs
playlist_name = '1live {:%Y-%m-%d %H:%M:%S}'.format(datetime.now())
playlist_id = api.create_playlist(playlist_name)
api.add_songs_to_playlist(playlist_id, storeIds)

print 'Added {} songs to playlist "{}".'.format(len(storeIds), playlist_name)
print '{} songs were extracted initially.'.format(len(songs), url)
print
print 'Songs not found in Google Play Music:'
for song in not_found_songs:
	print u'{s[artist]} - {s[title]}'.format(s=song)
class MusicSync(object):
    def __init__(self, email=None, password=None):
        self.mm = Musicmanager()
        self.wc = Webclient()
        self.mc = Mobileclient()
        if not email:
            email = raw_input("Email: ")
        if not password:
            password = getpass()

        self.email = email
        self.password = password

        self.logged_in = self.auth()

        print "Fetching playlists from Google..."
        self.playlists = self.mc.get_all_user_playlist_contents()
        #self.playlists = self.mc.get_all_playlists()
        #self.playlists = self.wc.get_all_playlist_ids(auto=False)
        self.all_songs = self.mc.get_all_songs()
        #print "Got %d playlists." % len(self.playlists['user'])
        print "Got %d playlists containing %d songs." % (len(
            self.playlists), len(self.all_songs))
        print ""

    def auth(self):
        self.logged_in = self.mc.login(self.email, self.password)
        #self.logged_in = self.wc.login(self.email, self.password)
        if not self.logged_in:
            print "Login failed..."
            exit()

        print ""
        print "Logged in as %s" % self.email
        print ""

        if not os.path.isfile(OAUTH_FILEPATH):
            print "First time login. Please follow the instructions below:"
            self.mm.perform_oauth()
        self.logged_in = self.mm.login()
        if not self.logged_in:
            print "OAuth failed... try deleting your %s file and trying again." % OAUTH_FILEPATH
            exit()

        print "Authenticated"
        print ""

    def sync_playlist(self, filename, remove_missing):
        #def sync_playlist(self, filename, remove_missing=False):
        filename = self.get_platform_path(filename)
        os.chdir(os.path.dirname(filename))
        title = os.path.splitext(os.path.basename(filename))[0]
        print "Syncing playlist: %s" % filename
        #if title not in self.playlists['user']:
        #print "   didn't exist... creating..."
        #self.playlists['user'][title] = [self.wc.create_playlist(title)]
        print ""

        plid = ""

        for pl in self.playlists:
            if pl['name'] == title:
                plid = pl['id']
                goog_songs = pl['tracks']

        if plid == "":
            print "   didn't exist... creating..."
            plid = self.mc.create_playlist(self, title)

        #plid = self.playlists['user'][title][0]
        #goog_songs = self.wc.get_playlist_songs(plid)
        print "%d songs already in Google Music playlist" % len(goog_songs)
        pc_songs = self.get_files_from_playlist(filename)
        print "%d songs in local playlist" % len(pc_songs)
        print ""

        # Sanity check max 1000 songs per playlist
        if len(pc_songs) > MAX_SONGS_IN_PLAYLIST:
            print "    Google music doesn't allow more than %d songs in a playlist..." % MAX_SONGS_IN_PLAYLIST
            print "    Will only attempt to sync the first %d songs." % MAX_SONGS_IN_PLAYLIST
            del pc_songs[MAX_SONGS_IN_PLAYLIST:]

        existing_files = 0
        added_files = 0
        failed_files = 0
        removed_files = 0
        fatal_count = 0

        for fn in pc_songs:
            if self.file_already_in_list(fn, goog_songs, self.all_songs):
                existing_files += 1
                continue
            print ""
            print "Adding: %s" % os.path.basename(fn).encode('cp1252')
            #print "Adding: %s" % os.path.basename(fn)
            #online = False
            online = self.find_song(fn, goog_songs, self.all_songs)
            #online = self.find_song(fn)
            song_id = None
            if online:
                song_id = online['id']
                print "   already uploaded [%s]" % song_id
            else:
                attempts = 0
                result = []
                while not result and attempts < MAX_UPLOAD_ATTEMPTS_PER_FILE:
                    print "   uploading... (may take a while)"
                    attempts += 1
                    try:
                        result = self.mm.upload(fn)
                    except (BadStatusLine, CannotSendRequest):
                        # Bail out if we're getting too many disconnects
                        if fatal_count >= MAX_CONNECTION_ERRORS_BEFORE_QUIT:
                            print ""
                            print "Too many disconnections - quitting. Please try running the script again."
                            print ""
                            exit()

                        print "Connection Error -- Reattempting login"
                        fatal_count += 1
                        self.wc.logout()
                        self.mc.logout()
                        self.mm.logout()
                        result = []
                        time.sleep(STANDARD_SLEEP)

                    except:
                        result = []
                        time.sleep(STANDARD_SLEEP)

                try:
                    if result[0]:
                        song_id = result[0].itervalues().next()
                    else:
                        song_id = result[1].itervalues().next()
                    print "   upload complete [%s]" % song_id
                except:
                    print "      upload failed - skipping"
                    tag = self.get_id3_tag(fn)
                    print "      failed song:\t%s\t%s\t%s" % (
                        tag['title'].encode('cp1252'),
                        tag['artist'].encode('cp1252'),
                        tag['album'].encode('cp1252'))

            if not song_id:
                failed_files += 1
                continue

            added = self.mc.add_songs_to_playlist(plid, song_id)
            time.sleep(.3)  # Don't spam the server too fast...
            print "   done adding to playlist"
            added_files += 1

        if remove_missing:
            for g in goog_songs:
                for s in self.all_songs:
                    if g['trackId'] == s['id']:
                        print ""
                        print "Removing: %s" % s['title'].encode('cp1252')
                        self.mc.remove_entries_from_playlist(g['id'])
                        #self.wc.remove_songs_from_playlist(plid, s.id)
                        time.sleep(.3)  # Don't spam the server too fast...
                        removed_files += 1

        print ""
        print "---"
        print "%d songs unmodified" % existing_files
        print "%d songs added" % added_files
        print "%d songs failed" % failed_files
        print "%d songs removed" % removed_files

    def get_files_from_playlist(self, filename):
        files = []
        f = codecs.open(filename, encoding='cp1252')
        #f = codecs.open(filename, encoding='utf-8')
        for line in f:
            line = line.rstrip().replace(u'\ufeff', u'')
            if line == "" or line[0] == "#":
                continue
            path = os.path.abspath(self.get_platform_path(line))
            if not os.path.exists(path):
                print "File not found: %s" % line
                continue
            files.append(path)
        f.close()
        return files

    def file_already_in_list(self, filename, goog_songs, all_songs):
        tag = self.get_id3_tag(filename)
        print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'),
                                             tag['artist'].encode('cp1252'),
                                             tag['album'].encode('cp1252'))
        i = 0
        while i < len(goog_songs):
            for s in all_songs:
                if goog_songs[i]['trackId'] == s['id']:
                    if self.tag_compare(s, tag):
                        print "Found match\t%s\t%s\t%s" % (
                            s['title'].encode('cp1252'),
                            s['artist'].encode('cp1252'),
                            s['album'].encode('cp1252'))
                        goog_songs.pop(i)
                        return True
            i += 1
        return False

    def get_id3_tag(self, filename):
        data = mutagen.File(filename, easy=True)
        r = {}
        if 'title' not in data:
            title = os.path.splitext(os.path.basename(filename))[0]
            print 'Found song with no ID3 title, setting using filename:'
            print '  %s' % title
            print '  (please note - the id3 format used (v2.4) is invisible to windows)'
            data['title'] = [title]
            data.save()
        r['title'] = data['title'][0]
        r['track'] = int(data['tracknumber'][0].split('/')
                         [0]) if 'tracknumber' in data else 0
        # If there is no track, try and get a track number off the front of the file... since thats
        # what google seems to do...
        # Not sure how google expects it to be formatted, for now this is a best guess
        if r['track'] == 0:
            m = re.match("(\d+) ", os.path.basename(filename))
            if m:
                r['track'] = int(m.group(0))
        r['artist'] = data['artist'][0] if 'artist' in data else ''
        r['album'] = data['album'][0] if 'album' in data else ''
        return r

    def find_song(self, filename, goog_songs, all_songs):
        tag = self.get_id3_tag(filename)
        print "Searching for\t%s\t%s\t%s" % (tag['title'].encode('cp1252'),
                                             tag['artist'].encode('cp1252'),
                                             tag['album'].encode('cp1252'))
        #results = self.wc.search(tag['title'])
        # NOTE - diagnostic print here to check results if you're creating duplicates
        #print results['song_hits']
        #for r in goog_songs:
        #for r in results['song_hits']:
        for s in all_songs:
            #if r['trackId'] == s['id']:
            if self.tag_compare(s, tag):
                # TODO: add rough time check to make sure its "close"
                print "Found match\t%s\t%s\t%s" % (
                    s['title'].encode('cp1252'), s['artist'].encode('cp1252'),
                    s['album'].encode('cp1252'))
                return s
        return None

    def tag_compare(self, g_song, tag):
        # If a google result has no track, google doesn't return a field for it
        if 'title' not in g_song:
            g_song['title'] = ""
        if 'artist' not in g_song:
            g_song['artist'] = ""
        if 'album' not in g_song:
            g_song['album'] = ""
        if 'track' not in g_song:
            g_song['track'] = 0
        if (g_song['title'].lower() == tag['title'].lower() and g_song['artist'].lower() == tag['artist'].lower()) or\
           (g_song['album'].lower() == tag['album'].lower() and g_song['title'].lower() == tag['title'].lower()) or\
           (g_song['artist'].lower() == tag['artist'].lower() and g_song['album'].lower() == tag['album'].lower() and g_song['track'] == tag['track']):
            print "Partial match\t%s\t%s\t%s" % (
                g_song['title'].encode('cp1252'),
                g_song['artist'].encode('cp1252'),
                g_song['album'].encode('cp1252'))
        return g_song['title'].lower() == tag['title'].lower() and\
               g_song['artist'].lower() == tag['artist'].lower() and\
               g_song['album'].lower() == tag['album'].lower() #and\
        #g_song['track'] == tag['track']

    def delete_song(self, sid):
        self.mc.delete_songs(sid)
        print "Deleted song by id [%s]" % sid

    def get_platform_path(self, full_path):
        # Try to avoid messing with the path if possible
        if os.sep == '/' and '\\' not in full_path:
            return full_path
        if os.sep == '\\' and '\\' in full_path:
            return full_path
        if '\\' not in full_path:
            return full_path
        return os.path.normpath(full_path.replace('\\', '/'))
                        explicit, args.explicit):

                    #If the length indicates we're working with a real song, then go ahead and cautiously grab the information we think we need. Not all tracks have all this information.
                    try:
                        store_id = track["storeId"]
                    except:
                        print("No Store ID for Query")
                    try:
                        artist_id = track["artistId"]
                    except:
                        print("No Artist ID for Query")

                    try:
                        #Add the song to Play Music library
                        mc.add_store_tracks([store_id])
                        mc.add_songs_to_playlist(playlist_id, store_id)
                        print("Added song: \"" + title + "\" by " + artist +
                              " to playlist: " + playlist_name)
                        print("Song artist: " + artist + ", album_artist: " +
                              album_artist)
                        #Break out of the loop cause we don't need to check any of the other results
                        break

                    except:
                        print("Unable to add song: \"" + title + "\" by " +
                              artist + " to playlist: " + playlist_name)
                        #Don't break out of the loop if we were unable to add this song, since then we can keep going through the rest of the songs in the list in case we find one we CAN add

        else:
            print("No results found, moving to next search query")
    else:
Example #31
0
                     secret.CATEGORY_A, secret.CATEGORY_B)
]
big_playlist = [
    p for p in playlists_to_use if p['name'] == secret.BIG_PLAYLIST
][0]
uncategorized = [
    p for p in playlists_to_use if p['name'] == secret.UNCATEGORIZED
][0]
category_a = [p for p in playlists_to_use if p['name'] == secret.CATEGORY_A][0]
category_b = [p for p in playlists_to_use if p['name'] == secret.CATEGORY_B][0]

# Create sets of song ids
all_songs_set = {song['trackId'] for song in big_playlist['tracks']}
cat_a_songs = {song['trackId'] for song in category_a['tracks']}
cat_b_songs = {song['trackId'] for song in category_b['tracks']}

# Set of all categorized songs
categorized_songs = cat_a_songs | cat_b_songs
uncategorized_songs = all_songs_set - categorized_songs
uncategorized_songs = list(uncategorized_songs)

# Add songs to playlist
uncategorized_id = uncategorized['id']

print("Adding uncategorized songs to playlist named", secret.UNCATEGORIZED)
api.add_songs_to_playlist(uncategorized_id, uncategorized_songs)
print("Success! Added", len(uncategorized_songs), "songs to playlist named",
      secret.UNCATEGORIZED)
api.logout()
print("Logged out")
count=0
old_playlist_id=""
songs = api.get_all_songs()
query = ("select a.album_name, s.song_name, s.filename, p.playlist_name from playlist p, playlist_song ps, song s, album a where p.playlist_id = ps.playlist_id and s.album_id = a.album_id and ps.song_id = s.song_id order by p.playlist_name")
playlistsongcursor.execute(query)


#### TODO - album_name and song_name is not unique...
for (album_name,  song_name, filename, playlist_name) in playlistsongcursor:
    for song in songs: 
        if song['album'] == album_name and song['title'] == song_name:
            # Get the playlist id we are adding to
            playlists = api.get_all_playlists()            
            for playlist in playlists:
                if count == 0:
                    old_playlist_id = playlist['id']
                    count=1;
                if playlist['name'] == playlist_name:
                    playlist_id = playlist['id']
                    if playlist_id != old_playlist_id:
                        # We have a complete playlist add it
                        print("Commiting playlist:", [old_playlist_id])   
                        api.add_songs_to_playlist(old_playlist_id, playlistsongs)
                        old_playlist_id = playlist_id
                        playlistsongs = []                                       
                    print("Adding song:", song['title'], [song['id']], "to playlist:", playlist_name, [playlist_id])   
                    playlistsongs.append(song['id'])
# Add the last playlist                   
api.add_songs_to_playlist(playlist_id, playlistsongs)
playlistsongcursor.close()
                    if (f[2] == 0
                            or (f[3] == False and song['rating'] == '5')):
                        if (song['artist'] == f[0] and song['album'] == f[1]
                                and song['playCount'] == i):
                            bad = False
                            for title in f[4]:
                                if title == song['title']:
                                    bad = True
                            if bad == False:
                                f[2] = song['id']
                                if song['rating'] == '5':
                                    f[3] = True
                                print(song['artist'])
                except:
                    if (song['artist'] == f[0] and song['album'] == f[1]
                            and song['playCount'] == i and f[2] == 0):
                        f[2] = song['id']
                        print('happen')
                    pass
    for f in fast:
        if f[2] != 0:
            api.add_songs_to_playlist(fastId, f[2])
    for f in slow:
        if f[2] != 0:
            api.add_songs_to_playlist(slowId, f[2])

print("This line will be printed.")

if __name__ == '__main__':
    pass
Example #34
0
    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)
Example #35
0
def reverseplaylist(playlist_name='', repeat=False, quick=False):
    mc = Mobileclient()
    mc.__init__()

    # Find location of this script
    dir_path = os.path.dirname(os.path.realpath(__file__))

    # Check whether this is the first time the program has been run by searching the directory for the log file
    file_list = os.listdir(dir_path)

    # No log file means not run
    if '.gmusiclog.txt' not in file_list:
        print(
            '\n' +
            'This is the first time this program has been run in this folder.'
            + '\n' + 'performing initial authentication.' + '\n')

        # Autorepeat cannot be true without logfile, override if true
        repeat = False

        # Start with a bogus device ID to determine real id from error message
        devID = 'ffffffff'
        mc.perform_oauth()
        try:
            mc.oauth_login(devID)
        except Exception as e:
            error = str(e)
            IDpos = error.find('*')
            nextIDpos = error.find('\n', IDpos + 1, len(error))
            devID = error[IDpos + 2:nextIDpos]
            # Perform login
            mc.oauth_login(devID)

        # Write authentication stuff to logfile
        with open(dir_path + '/.gmusiclog.txt', 'w') as log:
            log.write(devID + '\n')
            x = datetime.datetime.now()
            timeString = (str(x.day) + '/' + str(x.month) + '/' + str(x.year) +
                          '  ' + str(x.hour) + ':' + str(x.minute) + ':' +
                          str(x.second) + '.' + str(x.microsecond))
            log.write('Initial authentication performed at ' + timeString +
                      '\n')

    # Log file exists, we will check whether the user has requested autorepeat
    else:
        print(
            '\n' +
            'This is not the first time this program has been run in this folder'
            + '\n' + 'performing login.' + '\n')

        # Open the logfile to read device id and previous playlists
        with open(dir_path + '/.gmusiclog.txt', 'r') as log:
            # Get device ID
            devID = log.readline().strip()
            # Look for the playlist name from the bottom of the list
            contents = log.read()

        # Perform login
        mc.oauth_login(devID)
        playlistLocation = contents.rfind('PLAYLIST')
        if playlistLocation != -1:
            # Define end of playlist to make defining desired playlist a little cleaner
            endOfPlaylist = contents.find('\n', playlistLocation,
                                          len(contents))
            desired_playlist = contents[playlistLocation + 10:endOfPlaylist]

        with open(dir_path + '/.gmusiclog.txt', 'a+') as log:

            # Write authentication stuff to logfile
            x = datetime.datetime.now()
            timeString = (str(x.day) + '/' + str(x.month) + '/' + str(x.year) +
                          '  ' + str(x.hour) + ':' + str(x.minute) + ':' +
                          str(x.second) + '.' + str(x.microsecond))
            log.write('Login performed at ' + timeString + '\n')

    # Get user input for desired playlist if autorepeat is not enabled
    if repeat == False and playlist_name == '':
        desired_playlist = input(
            'Name of the playlist being reversed (case sensetive): ')
    elif playlist_name != '':
        # If a name is given this overrides all else
        desired_playlist = playlist_name
    else:
        print('Autorepeat enabled, reversing playlist: ' + desired_playlist +
              '\n')

    # Establish the name of the reversed playlist
    reversed_playlist = desired_playlist + 'REVERSED'

    # Check to see whether the desired and reversed playlists exist yet
    allPlaylists = mc.get_all_playlists()
    desired_playlist_index = -1
    reversed_playlist_index = -1
    for n in allPlaylists:
        if n['name'] == reversed_playlist:
            reversed_playlist_index = n
            reversedID = n['id']
        elif n['name'] == desired_playlist:
            desired_playlist_index = n
            desiredID = n['id']

    # Desired playlist exists, so we check to see if it has also been reversed
    if desired_playlist_index != -1:
        # We cache the playlist name so that it can be automatically re-reversed next time
        with open(dir_path + '/.gmusiclog.txt', 'a+') as log:
            log.write('PLAYLIST: ' + desired_playlist + '\n')

        # Desired playlist has been reversed, we can either delete the old one before proceeding
        # or perform a quick update
        if reversed_playlist_index != -1:
            print('The ' + desired_playlist + ' playlist has been reversed.')

            # Determine whether to do a quick update or not
            if quick == True:
                print('performing quick update')
                quick_update(mc, desiredID, reversedID)
                return
            else:
                print('Performing full update\n' +
                      'Deleting the old playlist...\n')
                mc.delete_playlist(reversedID)

        # Desired playlist has not been reversed, create the reverse
        else:
            print('The ' + desired_playlist +
                  ' playlist has not been reversed, creating the reverse now.')

        # If we have got this far, the reversed playlist doesn't exist
        print('Generating reversed song list...')
        reversedID, id_list = create_new(mc, desired_playlist)
        print('Adding songs to the playlist...')
        mc.add_songs_to_playlist(reversedID, id_list)
        print('Done!')

    # No such playlist exists
    else:
        print(
            'No playlist by the name of ' + desired_playlist +
            ' found. Did you spell it correctly? A reminder here that the playlist name is case sensetive.'
        )
Example #36
0
    # an artist/track pair from.
    for search in plain_search:
        match = plain_search_match(search)
        if match is None:
            no_matches.append((search, search))
        else:
            if match not in songs:
                songs.append(match)

    return songs, no_matches


if logged_in:

    logger.info("Logged in")

    for subReddit, parserConfigurations in config.subreddits.iteritems():
        logger.info("Generating songs for " + subReddit)

        # A subreddit may have multiple playlist configurations.
        for configuration in parserConfigurations:
            validate_configuration(configuration)
            posts = find_posts_in_subreddit(subReddit, configuration)
            songs, no_matches = find_songs_from_submissions(posts, configuration)
            playlist_id = create_or_update_playlist(configuration)

            logger.info("Number of songs matched: {}".format(len(songs)))
            logger.info("No matches for: {}".format(no_matches))

            gmusic.add_songs_to_playlist(playlist_id, songs)
Example #37
0
    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)
Example #38
0
            add_search_string(tracks, search_strings)
else:
    print("Can't get token for", username)

print(f'Detected {len(search_strings)} songs to transfer.')

mm = Mobileclient()
mm.perform_oauth()
mm.oauth_login(Mobileclient.FROM_MAC_ADDRESS)
playlist_id = mm.create_playlist(playlist_name)

print(f'Playlist \'{playlist_name}\' created.')
found_songs = 0
for row in search_strings:
    print(f'\t Searching \'{row}\'.')

    search_result = mm.search(row)
    songs = search_result.get('song_hits')

    song_id = None
    if len(songs) > 0:
        song_id = songs[0].get('track').get('storeId')
        found_songs += 1
    else:
        print('Song not found.')
        continue

    mm.add_songs_to_playlist(playlist_id, song_id)

print(f'Imported {found_songs} songs.')
                # mismatch between sound count and data returned by API?
                print episode['name'].encode("utf-8"), ' songs: ', len(data['songs']), '/', episode['song_count']

                # for each song
                for song in data['songs']:
                    artist_name = song['artist']['name']
                    song_name = song['name']

                    if not artist_name or not song_name:
                        continue
                    else:
                        artist_name = artist_name.encode("utf-8")
                        song_name = song_name.encode("utf-8")

                    search_song = "{} - {}".format(artist_name, song_name)

                    # get song ID
                    song_id = get_song_id(search_song)
                    if song_id:
                        song_ids.append(song_id)

                    print
                        
                # add season of music to playlist 
                print 'Adding to playlist: ' + show
                api.add_songs_to_playlist(playlist_id, song_ids)

                # clear song list
                song_ids[:] = []
Example #40
0
class GMusic(object):
    def __init__(self):
        self.authenticated = False
        self.all_access = False
        self.library_loaded = False
        self.all_songs = []
        self.letters = {}
        self.artists = {}
        self.albums = {}
        self.genres = {}
        self.tracks_by_letter = {}
        self.tracks_by_artist = {}
        self.tracks_by_album = {}
        self.tracks_by_genre = {}
        self._device = None
        self._webclient = Webclient(debug_logging=False)
        self._mobileclient = Mobileclient(debug_logging=False)
        self._playlists = []
        self._playlist_contents = []
        self._stations = []

    def _get_device_id(self):
        if self.authenticated:
            devices = self._webclient.get_registered_devices()
            for dev in devices:
                if dev['type'] == 'PHONE':
                    self._device = dev['id'][2:]
                    break
                elif dev['type'] == 'IOS':
                    self._device = dev['id']
                    break

    def _set_all_access(self):
        settings = self._webclient._make_call(webclient.GetSettings, '')
        self.all_access = True if 'isSubscription' in settings[
            'settings'] and settings['settings'][
                'isSubscription'] == True else False

    def _set_all_songs(self):
        if len(self.all_songs) == 0:
            try:
                self.all_songs = self._mobileclient.get_all_songs()
            except NotLoggedIn:
                if self.authenticate():
                    self.all_songs = self._mobileclient.get_all_songs()
                else:
                    return []

        else:
            return self.all_songs

    def authenticate(self, email, password):
        try:
            mcauthenticated = self._mobileclient.login(email, password)
        except AlreadyLoggedIn:
            mcauthenticated = True

        try:
            wcauthenticated = self._webclient.login(email, password)
        except AlreadyLoggedIn:
            wcauthenticated = True

        self.authenticated = mcauthenticated and wcauthenticated
        self._set_all_access()
        self._get_device_id()
        return self.authenticated

    def load_data(self):
        self._set_all_songs()
        for song in self.all_songs:
            thumb = None
            letter = song['title'][0]
            artist = song['artist']
            album = song['album']
            genre = song['genre'] if 'genre' in song else '(None)'

            if letter not in self.tracks_by_letter:
                self.tracks_by_letter[letter] = []
                self.letters[letter] = None

            if artist not in self.tracks_by_artist:
                self.tracks_by_artist[artist] = []
                self.artists[artist] = None

            if album not in self.tracks_by_album:
                self.tracks_by_album[album] = []
                self.albums[album] = None

            if genre not in self.tracks_by_genre:
                self.tracks_by_genre[genre] = []
                self.genres[genre] = None

            track = {'artist': artist, 'album': album}

            if 'title' in song:
                track['title'] = song['title']

            if 'album' in song:
                track['album'] = song['album']

            if 'artist' in song:
                track['artist'] = song['artist']

            if 'durationMillis' in song:
                track['durationMillis'] = song['durationMillis']

            if 'id' in song:
                track['id'] = song['id']

            if 'trackNumber' in song:
                track['trackType'] = song['trackNumber']

            if 'storeId' in song:
                track['storeId'] = song['storeId']

            if 'albumArtRef' in song:
                track['albumArtRef'] = song['albumArtRef']
                thumb = song['albumArtRef'][0]['url']
                self.letters[letter] = thumb
                self.artists[artist] = thumb
                self.albums[album] = thumb
                self.genres[genre] = thumb

            self.tracks_by_letter[letter].append({
                'track': track,
                'thumb': thumb,
                'id': song['id']
            })
            self.tracks_by_artist[artist].append({
                'track': track,
                'thumb': thumb,
                'id': song['id']
            })
            self.tracks_by_album[album].append({
                'track': track,
                'thumb': thumb,
                'id': song['id']
            })
            self.tracks_by_genre[genre].append({
                'track': track,
                'thumb': thumb,
                'id': song['id']
            })

        self.library_loaded = True

    def get_tracks_for_type(self, type, name):
        type = type.lower()
        if type == 'artists':
            return self.tracks_by_artist[name]
        elif type == 'albums':
            return self.tracks_by_album[name]
        elif type == 'genres':
            return self.tracks_by_genre[name]
        elif type == 'songs by letter':
            return self.tracks_by_letter[name]
        else:
            return {}

    def get_song(self, id):
        return [x for x in self.all_songs if x['id'] == id][0]

    def get_all_playlists(self):
        if len(self._playlists) == 0:
            try:
                self._playlists = self._mobileclient.get_all_playlists()
            except NotLoggedIn:
                if self.authenticate():
                    self._playlists = self._mobileclient.get_all_playlists()
                else:
                    return []

        return self._playlists

    def get_all_user_playlist_contents(self, id):
        tracks = []
        if len(self._playlist_contents) == 0:
            try:
                self._playlist_contents = self._mobileclient.get_all_user_playlist_contents(
                )
            except NotLoggedIn:
                if self.authenticate():
                    self._playlist_contents = self._mobileclient.get_all_user_playlist_contents(
                    )
                else:
                    return []

        for playlist in self._playlist_contents:
            if id == playlist['id']:
                tracks = playlist['tracks']
                break

        return tracks

    def get_shared_playlist_contents(self, token):
        playlist = []
        try:
            playlist = self._mobileclient.get_shared_playlist_contents(token)
        except NotLoggedIn:
            if self.authenticate():
                playlist = self._mobileclient.get_shared_playlist_contents(
                    token)
            else:
                return []

        return playlist

    def get_all_stations(self):
        if len(self._stations) == 0:
            try:
                self._stations = self._mobileclient.get_all_stations()
            except NotLoggedIn:
                if self.authenticate():
                    self._stations = self._mobileclient.get_all_stations()
                else:
                    return []

        return self._stations

    def get_station_tracks(self, id, num_tracks=200):
        tracks = []
        try:
            tracks = self._mobileclient.get_station_tracks(id, num_tracks)
        except NotLoggedIn:
            if self.authenticate():
                tracks = self._mobileclient.get_station_tracks(id, num_tracks)
            else:
                return []

        return tracks

    def get_genres(self):
        genres = []
        try:
            genres = self._mobileclient.get_genres()
        except NotLoggedIn:
            if self.authenticate():
                genres = self._mobileclient.get_genres()
            else:
                return []

        return genres

    def create_station(self, name, id):
        station = None
        try:
            station = self._mobileclient.create_station(name=name, genre_id=id)
        except NotLoggedIn:
            if self.authenticate():
                station = self._mobileclient.create_station(name=name,
                                                            genre_id=id)
            else:
                return []

        return station

    def search_all_access(self, query, max_results=50):
        results = None
        try:
            results = self._mobileclient.search_all_access(query, max_results)
        except NotLoggedIn:
            if self.authenticate():
                results = self._mobileclient.search_all_access(
                    query, max_results)
            else:
                return []

        return results

    def get_artist_info(self,
                        id,
                        include_albums=True,
                        max_top_tracks=5,
                        max_rel_artist=5):
        results = None
        try:
            results = self._mobileclient.get_artist_info(
                id,
                include_albums=include_albums,
                max_top_tracks=max_top_tracks,
                max_rel_artist=max_rel_artist)
        except NotLoggedIn:
            if self.authenticate():
                results = self._mobileclient.get_artist_info(
                    id,
                    include_albums=include_albums,
                    max_top_tracks=max_top_tracks,
                    max_rel_artist=max_rel_artist)
            else:
                return []

        return results

    def get_album_info(self, id, include_tracks=True):
        results = None
        try:
            results = self._mobileclient.get_album_info(
                id, include_tracks=include_tracks)
        except NotLoggedIn:
            if self.authenticate():
                results = self._mobileclient.get_album_info(
                    id, include_tracks=include_tracks)
            else:
                return []

        return results

    def add_aa_track(self, id):
        track = None
        try:
            track = self._mobileclient.add_aa_track(id)
        except NotLoggedIn:
            if self.authenticate():
                track = self._mobileclient.add_aa_track(id)
            else:
                return None

        return track

    def add_songs_to_playlist(self, playlist_id, song_ids):
        tracks = None
        try:
            tracks = self._mobileclient.add_songs_to_playlist(
                playlist_id, song_ids)
        except NotLoggedIn:
            if self.authenticate():
                tracks = self._mobileclient.add_songs_to_playlist(
                    playlist_id, song_ids)
            else:
                return None

        return tracks

    def get_stream_url(self, id):
        try:
            stream_url = self._mobileclient.get_stream_url(id, self._device)
        except NotLoggedIn:
            if self.authenticate():
                stream_url = self._mobileclient.get_stream_url(
                    id, self._device)
            else:
                return ''
        except CallFailure:
            raise CallFailure('Could not play song with id: ' + id,
                              'get_stream_url')

        return stream_url
			data = get(season_url)

			# for each episode
			for episode in data['episodes']:
				
				data = get(season_url+'/'+episode['id'])

				# mismatch between sound count and data returned by API?
				print episode['name'].encode("utf-8"), ' songs: ', len(data['songs']), '/', episode['song_count']

				# for each song
				for song in data['songs']:
					#if(song['confidence'] == 'high'):
					search_song = song['artist']['name'] + ' - ' + song['name']

					# get song ID
					id = get_song_id(search_song)
					if(id):
						song_ids.append(id)
					#else:
					#	print search_song + ' (not found)'
				print
				
			# add season of music to playlist 
			print 'Adding to playlist: ' + show
			api.add_songs_to_playlist(playlist_id, song_ids)

			# clear song list
			song_ids[:] = []
	
from gmusicapi import Mobileclient
from grooveshark import Client
import sys

api = Mobileclient()
api.login(sys.argv[1], sys.argv[2])

groove_client = Client()

playlist = groove_client.playlist(sys.argv[3])

gp_songs=[]

for song in playlist.songs:
    query = song.name + " " + song.artist.name
    gp_query_result = api.search_all_access( query, 1)
    try:
        track = gp_query_result['song_hits'][0]['track']
        print( "Adding " + track['title'] + " by " +  track['artist'] )
        gp_songs.append(track['nid'])
    except IndexError as e:
        print("Coudn't find " + query);
        pass
    except:
        print(e)
        pass

gp_playlist = api.create_playlist(playlist.name)
api.add_songs_to_playlist(gp_playlist, gp_songs)
class GoogleMusicClient(object):

  def __init__(self, username, password):
    self._username = username
    self._password = password
    self._apiClient = None

  def initConnection(self):
    self._apiClient = Mobileclient(debug_logging=False)
    if not self._apiClient.login(self._username, self._password):
      raise RuntimeError("Could not connect %s to Google Music." % \
                          self._username)

  def addPlaylist(self, playlist):
    plid = self._apiClient.create_playlist(playlist.name)
    tids = []
    for track in playlist.tracks:
      aaid = self._getAllAccessTrackId(track)
      if aaid:
        try:
          tid = self._apiClient.add_aa_track(aaid)
          if tid:
            tids.append(tid)
          else:
            LOGGER.warning( "Could not add track %s to library.", str(track))
        except:
          LOGGER.error("Could not add track %s to library.", str(track))
          continue
      else:
        LOGGER.warning("Track %s not found.", str(track))
    self._apiClient.add_songs_to_playlist(plid, tids)

  def addAlbum(self, album):
    aaid = self._getAllAccessAlbumId(album)
    if aaid:
      albumInfo = self._apiClient.get_album_info(aaid, include_tracks=True)
      for track in albumInfo["tracks"]:
        try:
          self._apiClient.add_aa_track(track[Track.GM_ID_KEY])
        except:
          LOGGER.error("Could not add track %s to library.", str(track))
          continue
    else:
      LOGGER.warning("Album %s not found.", str(album))

  def _getAllAccessFirstResult(self, resource):
    queryStr = re.sub("[-:\(\)\",]","", "%s %s" % (resource.name,
                                                   resource.artist))
    queryStr = re.sub("\s+", "+", queryStr)
    searchResults = self._apiClient.search_all_access(queryStr)
    gmusicResources = searchResults.get(resource.GM_HITS_KEY)
    if gmusicResources:
      firstResult = gmusicResources[0][resource.GM_NAME]
      return firstResult[resource.GM_ID_KEY]
    else:
      return None

  def _getAllAccessTrackId(self, track):
    return self._getAllAccessFirstResult(track)

  def _getAllAccessAlbumId(self, album):
    return self._getAllAccessFirstResult(album)
Example #44
0
            song = song[:special_char] + song[special_char + 1:]
            special_char = song.find('\\')
        sTokens = song.split('`')
        song, duration = sTokens[0], int(sTokens[1])
        results = api.search_all_access(song, 10)['song_hits']
        song_id = findMatch(results, song, duration, explicit_mode,
                            matchHistory)
        if song_id is None:
            results = api.search_all_access(song.split('~')[0],
                                            10)['song_hits']
            song_id = findMatch(results, song, duration, explicit_mode,
                                matchHistory)
        if song_id is not None:
            if errcount >= 20:
                raise Exception(
                    'Too many errors have occurred - the server is not responding properly.'
                )
            while errcount < 20:
                try:
                    api.add_songs_to_playlist(newPID, song_id)
                    break
                except gmusicapi.exceptions.CallFailure:
                    traceback.print_exc()
                    print
                    print 'was trying to look up ' + song
                    print 'it seems the api has failed to respond properly - waiting for some time.'
                    time.sleep(2)
                    errcount += 1
        else:
            print 'could not find ' + song
Example #45
0
                                       client_secret='YOUR CLIENT SECRET',
                                       redirect_uri='YOUR REDIRECT URI')

    if token:
        sp = spotipy.Spotify(auth=token)  # start and authorize spotipy
        tracks = get_playlist_tracks(
            username, playlist)  # get track, artist, and album names
    else:
        print "Can't get token for", username

    # ********************
    # Update GPM playlist:
    # ********************
    gpm = Mobileclient()
    gpm_username = '******'
    gpm_password = '******'
    logged_in = gpm.login(
        gpm_username, gpm_password,
        Mobileclient.FROM_MAC_ADDRESS)  # is true if log in successful

    if logged_in:
        gpm_song_ids, skipped_songs = get_song_ids(tracks)
        gpm_playlist_id = 'YOUR GPM PLAYLIST ID'  #gpm.create_playlist('Export to GPM Test', description='Test playlist', public=False)
        for i in range(len(gpm_song_ids)):
            gpm.add_songs_to_playlist(gpm_playlist_id, gpm_song_ids[i])

        outputf.write('Number of tracks not found: %s' % skipped_songs)
        write_list_to_file(skipped_songs)
    else:
        print "Can't log in to GPM. Check credentials or connection."
Example #46
0
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()
Example #47
0
class GMusicClient(ContentConsumer):
    """Element in charge of interfacing with GMusicApi Client"""

    def __init__(self, data_cache):
        self.client = Mobileclient()
        self.data_cache = data_cache

    def login(self):
        """Use data/unlocked/credentials.json to log in"""
        mac = Mobileclient.FROM_MAC_ADDRESS
        try:
            credentials = json.load(open("data/unlocked/credentials.json", "r"))
            result = self.client.login(credentials["username"], credentials["password"], mac)
            if result == False:
                print("\n\033[93mLogin failed.")
                print("Please double check that data/unlocked/credentials.json has correct information.\033[m\n")
                os._exit(1)
        except:
            print("\n\033[93mdata/unlocked/credentials.json is not valid.")
            print("You may need to delete and regnerate the credentials file.\033[m\n")
            exit(1)

    def load_my_library(self):
        """Load user's songs, playlists, and stations"""
        track_load = threading.Thread(target=self.load_tracks)
        radio_load = threading.Thread(target=self.load_radios)
        playlist_load = threading.Thread(target=self.load_playlists)

        track_load.start()
        radio_load.start()
        playlist_load.start()

        track_load.join()
        radio_load.join()
        playlist_load.join()

    def load_playlists(self):
        playlists = self.client.get_all_user_playlist_contents()
        playlists.reverse()
        self.data_cache.playlists = playlists

    def load_tracks(self):
        self.data_cache.tracks = [t for t in self.client.get_all_songs() if "nid" in t]

    def scrape_song(self, track):
        return track

    def load_radios(self):
        radios = self.client.get_all_stations()
        radios.reverse()
        self.data_cache.radios = radios

    def get_radio_contents(self, radio_id):
        tracks = self.client.get_station_tracks(radio_id)
        return tracks

    def get_radio_list(self, name):
        return [r for r in self.data_cache.radios if name in r["name"]]

    def filter(self, element, field, filter_by):
        return [e for e in element if filter_by in e[field]]

    def get_playlist_list(self, name):
        return self.filter(self.data_cache.playlists, "name", name)

    def search_all_access(self, query):
        return self.client.search_all_access(query)

    def create_radio(self, seed_type, id, name):
        """Create a radio"""
        # This is a weird way to do this, but there's no other choice
        ids = {"track": None, "album": None, "artist": None}
        seed_id_name = self.get_type_name(seed_type)
        ids[seed_id_name] = id
        return self.client.create_station(
            name=name, track_id=ids["track"], album_id=ids["album"], artist_id=ids["artist"]
        )

    def search_items_all_access(self, type, query):
        """Searches Albums, Artists, and Songs; uses metaprogramming"""
        index_arguments = self.get_index_arguments(type)

        items = self.search_all_access(query)["{0}_hits".format(type[:-1])]
        return [self.format_item(item, type, index_arguments) for item in items]

    def get_sub_items(self, type_from, search_type, from_id):
        """Here type_from refers to artist or album we're indexing against"""
        args = self.get_index_arguments(search_type)

        if type_from == "playlist":
            return self.get_playlist_contents(from_id)

        else:
            # Get the appropriate search method and execute it
            search_method_name = "get_{0}_info".format(type_from)
            search_method = getattr(self.client, search_method_name)
            try:
                items = search_method(from_id, True)[args["type"] + "s"]  # True here includes subelements
            except:
                # User passed in a bad id or something
                return

        # Now return appropriately
        return [self.format_subitems(t, args) for t in items if args["id"] in t]

    def get_playlist_contents(self, from_id):
        """Playlist exclusive stuff"""
        shareToken = [t for t in self.data_cache.playlists if t["id"] == from_id][0]["shareToken"]

        contents = self.client.get_shared_playlist_contents(shareToken)
        return [self.format_playlist_contents(t) for t in contents if "track" in t]

    def get_suggested(self):
        """Returns a list of tracks that the user might be interested in"""
        items = sorted(self.client.get_promoted_songs(), key=lambda y: y["title"])
        return [self.format_suggested(t) for t in items if "storeId" in t]

    def get_information_about(self, from_type, from_id):
        """Gets specific information about an id (depending on the type)"""
        if "artist" in from_type.lower():
            return self.client.get_artist_info(from_id, include_albums=False)
        if "album" in from_type.lower():
            return self.client.get_album_info(from_id, include_tracks=False)
        return self.client.get_track_info(from_id)

    def get_stream_url(self, nid):
        return self.client.get_stream_url(nid)

    def lookup(self, nid):
        return self.client.get_track_info(nid)

    def add_track_to_library(self, nid):
        self.client.add_aa_track(nid)

    def add_to_playlist(self, playlist_id, nid):
        self.client.add_songs_to_playlist(playlist_id, nid)

        playlist_load = threading.Thread(target=self.load_playlists)
        playlist_load.daemon = True
        playlist_load.start()

    def format_suggested(self, t):
        return (t["title"], t["storeId"], "Play", t["album"])

    def format_playlist_contents(self, t):
        return (t["track"]["title"], t["trackId"], "Play", t["track"]["album"])

    def format_subitems(self, t, args):
        return (t[args["name"]], t[args["id"]], args["command"], t[args["alt"]])
Example #48
0
            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')
Example #49
0
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)
Example #50
0
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)
Example #51
0
class GMusic(object):
    def __init__(self):
        self.mob_client = Mobileclient()
        self.web_client = Webclient()
        self.logfile = None
        self.logfile_open = False
        # logged_in is True if login was successful
        logged_in = self.mob_client.login(MY_GMAIL, MY_PASSWORD, Mobileclient.FROM_MAC_ADDRESS)
        if logged_in:
            print("GoogleMusic MobileClient Logged In")
        else:
            raise Exception("Couldn't log in, exiting...")
        logged_in = self.web_client.login(MY_GMAIL, MY_PASSWORD)
        if logged_in:
            print("GoogleMusic WebClient Logged In")
        else:
            raise Exception("Couldn't log in, exiting...")

    def build_play_list_dummy(self):
        library = self.mob_client.get_all_songs()
        tracks = [track for track in library if track['artist'] == 'Adhesive Wombat'
                  and "night shade" in track['title'].lower()]
        # for track in sweet_tracks:
        #     print(track)

        playlist_id = self.mob_client.create_playlist('Test playlist')
        for track in tracks:
            self.mob_client.add_songs_to_playlist(playlist_id, track['id'])

        return playlist_id

    def _setlogfile(self, logfile):
        if self.logfile_open:
            self._print_and_log("logfile {} already opened! Not opening again!")
        else:
            self.logfile = logfile
            with open(self.logfile, "w") as logfile:
                logfile.write("LOGSTART: {}, script: {}\n".format(asctime(localtime()), __file__))
            self.logfile_open = True

    def _print_and_log(self, msg):
        if self.logfile:
            with open(self.logfile, "a") as logfile:
                logfile.write(msg+"\n")
        print msg

    def find_duplicate_songs(self, outfile=None):
        duplicates = []
        if outfile:
            if path.exists(path.dirname(outfile)):
                self._setlogfile(outfile)
            else:
                raise IOError("Output filename given: {} is in an none-existing dir".format(outfile))

        library = self.mob_client.get_all_songs()
        tracks = [track for track in library]
        while tracks:
            track = tracks[0]
            dup_list = []
            dup_idx_list = []
            for idx, track_i in enumerate(tracks):
                if track['artist'].lower() == track_i['artist'].lower() and\
                   track['album'].lower() == track_i['album'].lower() and\
                    track['discNumber'] == track_i['discNumber'] and\
                    track['trackNumber'] == track_i['trackNumber'] and\
                   track['title'].lower() == track_i['title'].lower():
                    dup_idx_list.append(idx)
                    dup_list.append(track_i)
            # Remove them:
            for index in sorted(dup_idx_list, reverse=True):
                del tracks[index]
            if len(dup_list) > 1:
                duplicates.append(dup_list)
        for idx, dup_list in enumerate(duplicates):
            self._print_and_log("{}: '{}' was found {} times!".format(idx+1, dup_list[0]['title'].encode("utf-8"),
                                                                      len(dup_list)))
        self._print_and_log("Found a total of {} duplicate songs!".format(len(duplicates)))
        # Display important stuff
        for idx, track_list in enumerate(duplicates):
            self._print_and_log("{}: BAND: {}, NAME:  '{}'".format(idx+1, track_list[0]['artist'],
                                                                   track_list[0]['title'].encode("utf-8")))
            for el in track_list[0]:
                for track in track_list:
                    if el not in track:
                        track[el] = "NO VALUE"
                    if track[el] != track_list[0][el] and el not in ['id', 'url', 'recentTimestamp', 'storeId', 'nid', 'clientId']:
                        # unicode?
                        try:
                            r_val = track_list[0][el].encode("utf-8")
                        except:
                            r_val = track_list[0][el]
                        # unicode?
                        try:
                            l_val = track[el].encode("utf-8")
                        except:
                            l_val = track[el]

                        self._print_and_log("track_id {}: {}='{}'".format(track_list[0]['id'], el, r_val))
                        self._print_and_log("track_id {}: {}='{}'".format(track['id'], el, l_val))

            # raw_input("Press any key to continue...")
        return duplicates

    def delete_duplicates(self, duplicates):
        self._print_and_log("Cleaning duplicates [removing oldest of each duplicant]:")
        old_song_ids = []
        for idx, dup_list in enumerate(duplicates):
            self._print_and_log("{}: BAND: {}, NAME:  '{}'".format(idx+1, dup_list[0]['artist'],
                                                                   dup_list[0]['title'].encode("utf-8")))
            track_timstamp = None
            oldest_id = None
            for el in dup_list:
                if track_timstamp is None and oldest_id is None:
                    track_timstamp = el["timestamp"]
                    oldest_id = el["id"]
                elif el["timestamp"] < track_timstamp:
                    track_timstamp = el["timestamp"]
                    oldest_id = el["id"]
            # finished with dup_list - log oldest id:
            self._print_and_log("Will delete {}, track_id: {}".format(el["title"], el["id"]))
            old_song_ids.append(oldest_id)

        self.mob_client.delete_songs(old_song_ids)
        self._print_and_log("track_ids deleted:\n{}".format(old_song_ids))
from gmusicapi import Mobileclient

client = Mobileclient()
assert client.login('*****@*****.**', PASSWORD, Mobileclient.FROM_MAC_ADDRESS)
tracks = client.get_all_songs()
genres_to_merge = {'Alternative Rock', 'Alt. Rock'}
playlist_track_ids = [t['id'] for t in tracks if t['genre'] in genres_to_merge]
print "Creating playlist with %d tracks" % len(playlist_track_ids)
playlist_id = client.create_playlist('Alt Rock')
client.add_songs_to_playlist(playlist_id, playlist_track_ids)
Example #53
0
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
Example #54
0
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
Example #55
0
    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)
Example #57
0
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