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
def __get_all_playlists(gpm_client: Mobileclient) -> None:
    playlists = gpm_client.get_all_playlists()
    playlist_dict = {}
    for playlist in playlists:
        playlist_dict.update({playlist['name']: playlist['id']})

    pass
Example #3
0
class GMusicApp:
    def __init__(self):
        self.base_dir = os.path.dirname(os.path.realpath(__file__))
        self.cred_filename = os.path.join(self.base_dir, 'credentials.json')
        self.api = Mobileclient()

    @staticmethod
    def get_device_id():
        dev_id = os.getenv('DEVICE_ID')

        if not dev_id:
            raise Exception('Please fill the DEVICE_ID field in .env file')
        elif not re.match('^[a-f0-9]*$', dev_id):
            raise Exception('DEVICE_ID is incorrect')

        return dev_id

    def auth(self):
        try:
            device_id = self.get_device_id()
        except Exception as e:
            raise e

        if not os.path.exists(self.cred_filename):
            self.api.perform_oauth(self.cred_filename, False)

        return self.api.oauth_login(device_id, self.cred_filename)

    def run(self):
        playlists = self.api.get_all_playlists()
        print(playlists)
Example #4
0
def google_playlists(x):
	api = Mobileclient()
	api.login('*****@*****.**', 'rtqjkpidxwxddpur', Mobileclient.FROM_MAC_ADDRESS)
	all_playlists = api.get_all_playlists(incremental=False, include_deleted=False)
	dj_list = list(set(x['host']))
	for k, dj in enumerate(dj_list):
		# pull out subset of a given Dj
		subset = x.loc[x['host']==(dj)]
		print("\n   Analyzing "+dj+" Playlists...\n")
		# pull out subset of dj subset for a given month
		for i in np.arange(1, 12, 1):
			print('Now loading Month: '+str(i))
			artists = np.load(dj+'/'+'2015-'+str(i)+'-artists.npy')
			if len(artists) == 0:
				break
			titles  = np.load(dj+'/'+'2015-'+str(i)+'-titles.npy')
			# playlist_exists = False
			# playlist_name = 'KCRW DJ '+host+', Tracks of 2015-'+str(i)
			# print("Searching for playlist named: " + playlist_name)
			# for playlist in all_playlists:
			# 	if playlist['name'] == playlist_name:
			# 		playlist_exists = True
			# 		playlist_id = playlist['id']
			# 		print("Playlist exists, adding new songs to playlist: "+playlist_name)
			# if not playlist_exists:
			# 	playlist_id = api.create_playlist(playlist_name, description=None, public=False)
			# 	print("Playlist is new, creating playlist and adding new songs to: "+playlist_name)
			search_google(api, artists, titles)
Example #5
0
def google_playlists(x):
    api = Mobileclient()
    api.login('*****@*****.**', 'rtqjkpidxwxddpur',
              Mobileclient.FROM_MAC_ADDRESS)
    all_playlists = api.get_all_playlists(incremental=False,
                                          include_deleted=False)
    dj_list = list(set(x['host']))
    for k, dj in enumerate(dj_list):
        # pull out subset of a given Dj
        subset = x.loc[x['host'] == (dj)]
        print("\n   Analyzing " + dj + " Playlists...\n")
        # pull out subset of dj subset for a given month
        for i in np.arange(1, 12, 1):
            print('Now loading Month: ' + str(i))
            artists = np.load(dj + '/' + '2015-' + str(i) + '-artists.npy')
            if len(artists) == 0:
                break
            titles = np.load(dj + '/' + '2015-' + str(i) + '-titles.npy')
            # playlist_exists = False
            # playlist_name = 'KCRW DJ '+host+', Tracks of 2015-'+str(i)
            # print("Searching for playlist named: " + playlist_name)
            # for playlist in all_playlists:
            # 	if playlist['name'] == playlist_name:
            # 		playlist_exists = True
            # 		playlist_id = playlist['id']
            # 		print("Playlist exists, adding new songs to playlist: "+playlist_name)
            # if not playlist_exists:
            # 	playlist_id = api.create_playlist(playlist_name, description=None, public=False)
            # 	print("Playlist is new, creating playlist and adding new songs to: "+playlist_name)
            search_google(api, artists, titles)
Example #6
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 #7
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 #8
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 #9
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 #10
0
	def download_songs(self):
		api = Musicmanager()
		ip = urllib2.urlopen('http://ip.42.pl/raw').read() #Obtain your public IP address
		mac_binary = str(get_mac()) #Obtain binary MAC address
		temp = mac_binary.replace(':', '').replace('-', '').replace('.', '').upper()
		mac_h3x = temp[:2] + ":" + ":".join([temp[i] + temp[i+1] for i in range(2,12,2)]) #Convert MAC from 48bit int to hexadecimal string
		user = pwd.getpwuid(os.getuid())[0] #Get your system's username
		hostname = '<' + ip + '>' + '' + '(gmusicapi-{2.0.0})'
		Musicmanager.perform_oauth(storage_filepath='/home/' + user + '/.config/gmusicapi/oauth.cred', open_browser=False)
		api.login(oauth_credentials='/home/' + user + '/.config/gmusicapi/oauth.cred', uploader_id=mac_h3x, uploader_name=hostname)
		gmusicapi.clients.Musicmanager(debug_logging=True, validate=True)		
		playlist_id = raw_input("insert id: ")
		
		api_ = Mobileclient()
		api_.login(self.email, self.password)
		playlist_method = api_.get_all_playlists()
def create_playlist(playlist_name):

    if Mobileclient.is_authenticated(gpm):
        # the following prints all playlist names
        all_playlists = Mobileclient.get_all_playlists(gpm)
        for playlist in all_playlists:
            # print(playlist)
            temp = set()
            temp.add(playlist['name'])
            # print(temp)

        return gpm.create_playlist(playlist_name)
    else:
        print("Mobileclient could not authenticate.")

    Mobileclient.logout(gpm)
Example #12
0
def add_song_to_playlist(song, playlist_name):

    if Mobileclient.is_authenticated(gpm):

        # get_all_playlists() returns a dict object filled with
        # information about all of a user's playlists, and
        # each individual playlist is a dict as well.
        playlists = Mobileclient.get_all_playlists(gpm)
        # print playlists

        # one can get all playlist names by just accessing the
        # 'name' key of each playlist dict, as well as
        # compare the playlist name to a user given name
        playlist_pattern = re.compile(r'(?:.)*\s?(' + re.escape(playlist_name) + r')\s?(?:.)*', re.IGNORECASE)
        found = False

        for i in playlists:
            # print(i['name'])

            if re.match(playlist_pattern, i['name']):
                found = True
                print("Playlist found!")
                search = gpm.search(song, 1)
                
                for track in search['song_hits']:
                    temp = dict(track['track'])
                    # print(temp)
                    gpm.add_songs_to_playlist(i['id'], temp['storeId'])
                    print("Song " + temp['title'] + " was found, and placed in playlist: " + playlist_name)
                break

        if not found:
            i = create_playlist(playlist_name)
            print(playlist_name + ' was not found, but it was created.')

            search = gpm.search(song, 1)

            for track in search['song_hits']:
                temp = dict(track['track'])
                gpm.add_songs_to_playlist(i, temp['storeId'])
                print("Song " + temp['title'] + " was found, and placed in playlist: " + playlist_name)
    else:
        print('Mobileclient could not authenticate.')

    Mobileclient.logout(gpm)
Example #13
0
class tizgmusicproxy(object):
    """A class for logging into a Google Play Music account and retrieving song
    URLs.

    """

    all_songs_album_title = "All Songs"
    thumbs_up_playlist_name = "Thumbs Up"

    def __init__(self, email, password, device_id):
        self.__gmusic = Mobileclient()
        self.__email = email
        self.__device_id = device_id
        self.logged_in = False
        self.queue = list()
        self.queue_index = -1
        self.play_queue_order = list()
        self.play_modes = TizEnumeration(["NORMAL", "SHUFFLE"])
        self.current_play_mode = self.play_modes.NORMAL
        self.now_playing_song = None

        userdir = os.path.expanduser('~')
        tizconfig = os.path.join(userdir, ".config/tizonia/." + email + ".auth_token")
        auth_token = ""
        if os.path.isfile(tizconfig):
            with open(tizconfig, "r") as f:
                auth_token = pickle.load(f)
                if auth_token:
                    # 'Keep track of the auth token' workaround. See:
                    # https://github.com/diraimondo/gmusicproxy/issues/34#issuecomment-147359198
                    print_msg("[Google Play Music] [Authenticating] : " \
                              "'with cached auth token'")
                    self.__gmusic.android_id = device_id
                    self.__gmusic.session._authtoken = auth_token
                    self.__gmusic.session.is_authenticated = True
                    try:
                        self.__gmusic.get_registered_devices()
                    except CallFailure:
                        # The token has expired. Reset the client object
                        print_wrn("[Google Play Music] [Authenticating] : " \
                                  "'auth token expired'")
                        self.__gmusic = Mobileclient()
                        auth_token = ""

        if not auth_token:
            attempts = 0
            print_nfo("[Google Play Music] [Authenticating] : " \
                      "'with user credentials'")
            while not self.logged_in and attempts < 3:
                self.logged_in = self.__gmusic.login(email, password, device_id)
                attempts += 1

            with open(tizconfig, "a+") as f:
                f.truncate()
                pickle.dump(self.__gmusic.session._authtoken, f)

        self.library = CaseInsensitiveDict()
        self.song_map = CaseInsensitiveDict()
        self.playlists = CaseInsensitiveDict()
        self.stations = CaseInsensitiveDict()

    def logout(self):
        """ Reset the session to an unauthenticated, default state.

        """
        self.__gmusic.logout()

    def set_play_mode(self, mode):
        """ Set the playback mode.

        :param mode: curren tvalid values are "NORMAL" and "SHUFFLE"

        """
        self.current_play_mode = getattr(self.play_modes, mode)
        self.__update_play_queue_order()

    def current_song_title_and_artist(self):
        """ Retrieve the current track's title and artist name.

        """
        logging.info("current_song_title_and_artist")
        song = self.now_playing_song
        if song:
            title = to_ascii(self.now_playing_song.get('title'))
            artist = to_ascii(self.now_playing_song.get('artist'))
            logging.info("Now playing %s by %s", title, artist)
            return artist, title
        else:
            return '', ''

    def current_song_album_and_duration(self):
        """ Retrieve the current track's album and duration.

        """
        logging.info("current_song_album_and_duration")
        song = self.now_playing_song
        if song:
            album = to_ascii(self.now_playing_song.get('album'))
            duration = to_ascii \
                       (self.now_playing_song.get('durationMillis'))
            logging.info("album %s duration %s", album, duration)
            return album, int(duration)
        else:
            return '', 0

    def current_track_and_album_total(self):
        """Return the current track number and the total number of tracks in the
        album, if known.

        """
        logging.info("current_track_and_album_total")
        song = self.now_playing_song
        track = 0
        total = 0
        if song:
            try:
                track = self.now_playing_song['trackNumber']
                total = self.now_playing_song['totalTrackCount']
                logging.info("track number %s total tracks %s", track, total)
            except KeyError:
                logging.info("trackNumber or totalTrackCount : not found")
        else:
            logging.info("current_song_track_number_"
                         "and_total_tracks : not found")
        return track, total

    def current_song_year(self):
        """ Return the current track's year of publication.

        """
        logging.info("current_song_year")
        song = self.now_playing_song
        year = 0
        if song:
            try:
                year = song['year']
                logging.info("track year %s", year)
            except KeyError:
                logging.info("year : not found")
        else:
            logging.info("current_song_year : not found")
        return year

    def clear_queue(self):
        """ Clears the playback queue.

        """
        self.queue = list()
        self.queue_index = -1

    def enqueue_artist(self, arg):
        """ Search the user's library for tracks from the given artist and adds
        them to the playback queue.

        :param arg: an artist
        """
        try:
            self.__update_local_library()
            artist = None
            if arg not in self.library.keys():
                for name, art in self.library.iteritems():
                    if arg.lower() in name.lower():
                        artist = art
                        print_wrn("[Google Play Music] '{0}' not found. " \
                                  "Playing '{1}' instead." \
                                  .format(arg.encode('utf-8'), \
                                          name.encode('utf-8')))
                        break
                if not artist:
                    # Play some random artist from the library
                    random.seed()
                    artist = random.choice(self.library.keys())
                    artist = self.library[artist]
                    print_wrn("[Google Play Music] '{0}' not found. "\
                              "Feeling lucky?." \
                              .format(arg.encode('utf-8')))
            else:
                artist = self.library[arg]
            tracks_added = 0
            for album in artist:
                tracks_added += self.__enqueue_tracks(artist[album])
            print_wrn("[Google Play Music] Playing '{0}'." \
                      .format(to_ascii(artist)))
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Artist not found : {0}".format(arg))

    def enqueue_album(self, arg):
        """ Search the user's library for albums with a given name and adds
        them to the playback queue.

        """
        try:
            self.__update_local_library()
            album = None
            artist = None
            tentative_album = None
            tentative_artist = None
            for library_artist in self.library:
                for artist_album in self.library[library_artist]:
                    print_nfo("[Google Play Music] [Album] '{0}'." \
                              .format(to_ascii(artist_album)))
                    if not album:
                        if arg.lower() == artist_album.lower():
                            album = artist_album
                            artist = library_artist
                            break
                    if not tentative_album:
                        if arg.lower() in artist_album.lower():
                            tentative_album = artist_album
                            tentative_artist = library_artist
                if album:
                    break

            if not album and tentative_album:
                album = tentative_album
                artist = tentative_artist
                print_wrn("[Google Play Music] '{0}' not found. " \
                          "Playing '{1}' instead." \
                          .format(arg.encode('utf-8'), \
                          album.encode('utf-8')))
            if not album:
                # Play some random album from the library
                random.seed()
                artist = random.choice(self.library.keys())
                album = random.choice(self.library[artist].keys())
                print_wrn("[Google Play Music] '{0}' not found. "\
                          "Feeling lucky?." \
                          .format(arg.encode('utf-8')))

            if not album:
                raise KeyError("Album not found : {0}".format(arg))

            self.__enqueue_tracks(self.library[artist][album])
            print_wrn("[Google Play Music] Playing '{0}'." \
                      .format(to_ascii(album)))
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Album not found : {0}".format(arg))

    def enqueue_playlist(self, arg):
        """Search the user's library for playlists with a given name
        and adds the tracks of the first match to the playback queue.

        Requires Unlimited subscription.

        """
        try:
            self.__update_local_library()
            self.__update_playlists()
            self.__update_playlists_unlimited()
            playlist = None
            playlist_name = None
            for name, plist in self.playlists.items():
                print_nfo("[Google Play Music] [Playlist] '{0}'." \
                          .format(to_ascii(name)))
            if arg not in self.playlists.keys():
                for name, plist in self.playlists.iteritems():
                    if arg.lower() in name.lower():
                        playlist = plist
                        playlist_name = name
                        print_wrn("[Google Play Music] '{0}' not found. " \
                                  "Playing '{1}' instead." \
                                  .format(arg.encode('utf-8'), \
                                          to_ascii(name)))
                        break
                if not playlist:
                    # Play some random playlist from the library
                    random.seed()
                    playlist_name = random.choice(self.playlists.keys())
                    playlist = self.playlists[playlist_name]
                    print_wrn("[Google Play Music] '{0}' not found. "\
                              "Feeling lucky?." \
                              .format(arg.encode('utf-8')))
            else:
                playlist_name = arg
                playlist = self.playlists[arg]

            self.__enqueue_tracks(playlist)
            print_wrn("[Google Play Music] Playing '{0}'." \
                      .format(to_ascii(playlist_name)))
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Playlist not found : {0}".format(arg))

    def enqueue_station_unlimited(self, arg):
        """Search the user's library for a station with a given name
        and add its tracks to the playback queue.

        Requires Unlimited subscription.

        """
        try:
            # First try to find a suitable station in the user's library
            self.__enqueue_user_station_unlimited(arg)

            if not len(self.queue):
                # If no suitable station is found in the user's library, then
                # search google play unlimited for a potential match.
                self.__enqueue_station_unlimited(arg)

            if not len(self.queue):
                raise KeyError

        except KeyError:
            raise KeyError("Station not found : {0}".format(arg))

    def enqueue_genre_unlimited(self, arg):
        """Search Unlimited for a genre with a given name and add its
        tracks to the playback queue.

        Requires Unlimited subscription.

        """
        print_msg("[Google Play Music] [Retrieving genres] : '{0}'. " \
                  .format(self.__email))

        try:
            all_genres = list()
            root_genres = self.__gmusic.get_genres()
            second_tier_genres = list()
            for root_genre in root_genres:
                second_tier_genres += self.__gmusic.get_genres(root_genre['id'])
            all_genres += root_genres
            all_genres += second_tier_genres
            for genre in all_genres:
                print_nfo("[Google Play Music] [Genre] '{0}'." \
                          .format(to_ascii(genre['name'])))
            genre = dict()
            if arg not in all_genres:
                genre = next((g for g in all_genres \
                              if arg.lower() in to_ascii(g['name']).lower()), \
                             None)

            tracks_added = 0
            while not tracks_added:
                if not genre and len(all_genres):
                    # Play some random genre from the search results
                    random.seed()
                    genre = random.choice(all_genres)
                    print_wrn("[Google Play Music] '{0}' not found. "\
                              "Feeling lucky?." \
                              .format(arg.encode('utf-8')))

                genre_name = genre['name']
                genre_id = genre['id']
                station_id = self.__gmusic.create_station(genre_name, \
                                                          None, None, None, genre_id)
                num_tracks = 200
                tracks = self.__gmusic.get_station_tracks(station_id, num_tracks)
                tracks_added = self.__enqueue_tracks(tracks)
                logging.info("Added %d tracks from %s to queue", tracks_added, genre_name)
                if not tracks_added:
                    # This will produce another iteration in the loop
                    genre = None

            print_wrn("[Google Play Music] Playing '{0}'." \
                      .format(to_ascii(genre['name'])))
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Genre not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_situation_unlimited(self, arg):
        """Search Unlimited for a situation with a given name and add its
        tracks to the playback queue.

        Requires Unlimited subscription.

        """
        print_msg("[Google Play Music] [Retrieving situations] : '{0}'. " \
                  .format(self.__email))

        try:

            self.__enqueue_situation_unlimited(arg)

            if not len(self.queue):
                raise KeyError

            logging.info("Added %d tracks from %s to queue", \
                         len(self.queue), arg)

        except KeyError:
            raise KeyError("Situation not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_artist_unlimited(self, arg):
        """Search Unlimited for an artist and adds the artist's 200 top tracks to the
        playback queue.

        Requires Unlimited subscription.

        """
        try:
            artist = self.__gmusic_search(arg, 'artist')

            include_albums = False
            max_top_tracks = 200
            max_rel_artist = 0
            artist_tracks = dict()
            if artist:
                artist_tracks = self.__gmusic.get_artist_info \
                                (artist['artist']['artistId'],
                                 include_albums, max_top_tracks,
                                 max_rel_artist)['topTracks']
            if not artist_tracks:
                raise KeyError

            tracks_added = self.__enqueue_tracks(artist_tracks)
            logging.info("Added %d tracks from %s to queue", \
                         tracks_added, arg)
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Artist not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_album_unlimited(self, arg):
        """Search Unlimited for an album and add its tracks to the
        playback queue.

        Requires Unlimited subscription.

        """
        try:
            album = self.__gmusic_search(arg, 'album')
            album_tracks = dict()
            if album:
                album_tracks = self.__gmusic.get_album_info \
                               (album['album']['albumId'])['tracks']
            if not album_tracks:
                raise KeyError

            print_wrn("[Google Play Music] Playing '{0}'." \
                      .format((album['album']['name']).encode('utf-8')))

            tracks_added = self.__enqueue_tracks(album_tracks)
            logging.info("Added %d tracks from %s to queue", \
                         tracks_added, arg)
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Album not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_tracks_unlimited(self, arg):
        """ Search Unlimited for a track name and adds all the matching tracks
        to the playback queue.

        Requires Unlimited subscription.

        """
        print_msg("[Google Play Music] [Retrieving library] : '{0}'. " \
                  .format(self.__email))

        try:
            max_results = 200
            track_hits = self.__gmusic.search(arg, max_results)['song_hits']
            if not len(track_hits):
                # Do another search with an empty string
                track_hits = self.__gmusic.search("", max_results)['song_hits']
                print_wrn("[Google Play Music] '{0}' not found. "\
                          "Feeling lucky?." \
                          .format(arg.encode('utf-8')))

            tracks = list()
            for hit in track_hits:
                tracks.append(hit['track'])
            tracks_added = self.__enqueue_tracks(tracks)
            logging.info("Added %d tracks from %s to queue", \
                         tracks_added, arg)
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Playlist not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_promoted_tracks_unlimited(self):
        """ Retrieve the url of the next track in the playback queue.

        """
        try:
            tracks = self.__gmusic.get_promoted_songs()
            count = 0
            for track in tracks:
                store_track = self.__gmusic.get_track_info(track['storeId'])
                if u'id' not in store_track.keys():
                    store_track[u'id'] = store_track['nid']
                self.queue.append(store_track)
                count += 1
            if count == 0:
                print_wrn("[Google Play Music] Operation requires " \
                          "an Unlimited subscription.")
            logging.info("Added %d Unlimited promoted tracks to queue", \
                         count)
            self.__update_play_queue_order()
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def next_url(self):
        """ Retrieve the url of the next track in the playback queue.

        """
        if len(self.queue):
            self.queue_index += 1
            if (self.queue_index < len(self.queue)) \
               and (self.queue_index >= 0):
                next_song = self.queue[self.play_queue_order[self.queue_index]]
                return self.__retrieve_track_url(next_song)
            else:
                self.queue_index = -1
                return self.next_url()
        else:
            return ''

    def prev_url(self):
        """ Retrieve the url of the previous track in the playback queue.

        """
        if len(self.queue):
            self.queue_index -= 1
            if (self.queue_index < len(self.queue)) \
               and (self.queue_index >= 0):
                prev_song = self.queue[self.play_queue_order[self.queue_index]]
                return self.__retrieve_track_url(prev_song)
            else:
                self.queue_index = len(self.queue)
                return self.prev_url()
        else:
            return ''

    def __update_play_queue_order(self):
        """ Update the queue playback order.

        A sequential order is applied if the current play mode is "NORMAL" or a
        random order if current play mode is "SHUFFLE"

        """
        total_tracks = len(self.queue)
        if total_tracks:
            if not len(self.play_queue_order):
                # Create a sequential play order, if empty
                self.play_queue_order = range(total_tracks)
            if self.current_play_mode == self.play_modes.SHUFFLE:
                random.shuffle(self.play_queue_order)
            print_nfo("[Google Play Music] [Tracks in queue] '{0}'." \
                      .format(total_tracks))

    def __retrieve_track_url(self, song):
        """ Retrieve a song url

        """
        song_url = self.__gmusic.get_stream_url(song['id'], self.__device_id)
        try:
            self.now_playing_song = song
            return song_url
        except AttributeError:
            logging.info("Could not retrieve the song url!")
            raise

    def __update_local_library(self):
        """ Retrieve the songs and albums from the user's library

        """
        print_msg("[Google Play Music] [Retrieving library] : '{0}'. " \
                  .format(self.__email))

        songs = self.__gmusic.get_all_songs()
        self.playlists[self.thumbs_up_playlist_name] = list()

        # Retrieve the user's song library
        for song in songs:
            if "rating" in song and song['rating'] == "5":
                self.playlists[self.thumbs_up_playlist_name].append(song)

            song_id = song['id']
            song_artist = song['artist']
            song_album = song['album']

            self.song_map[song_id] = song

            if song_artist == "":
                song_artist = "Unknown Artist"

            if song_album == "":
                song_album = "Unknown Album"

            if song_artist not in self.library:
                self.library[song_artist] = CaseInsensitiveDict()
                self.library[song_artist][self.all_songs_album_title] = list()

            if song_album not in self.library[song_artist]:
                self.library[song_artist][song_album] = list()

            self.library[song_artist][song_album].append(song)
            self.library[song_artist][self.all_songs_album_title].append(song)

        # Sort albums by track number
        for artist in self.library.keys():
            logging.info("Artist : %s", to_ascii(artist))
            for album in self.library[artist].keys():
                logging.info("   Album : %s", to_ascii(album))
                if album == self.all_songs_album_title:
                    sorted_album = sorted(self.library[artist][album],
                                          key=lambda k: k['title'])
                else:
                    sorted_album = sorted(self.library[artist][album],
                                          key=lambda k: k.get('trackNumber',
                                                              0))
                self.library[artist][album] = sorted_album

    def __update_stations_unlimited(self):
        """ Retrieve stations (Unlimited)

        """
        self.stations.clear()
        stations = self.__gmusic.get_all_stations()
        self.stations[u"I'm Feeling Lucky"] = 'IFL'
        for station in stations:
            station_name = station['name']
            logging.info("station name : %s", to_ascii(station_name))
            self.stations[station_name] = station['id']

    def __enqueue_user_station_unlimited(self, arg):
        """ Enqueue a user station (Unlimited)

        """
        print_msg("[Google Play Music] [Station search "\
                  "in user's library] : '{0}'. " \
                  .format(self.__email))
        self.__update_stations_unlimited()
        station_name = arg
        station_id = None
        for name, st_id in self.stations.iteritems():
            print_nfo("[Google Play Music] [Station] '{0}'." \
                      .format(to_ascii(name)))
        if arg not in self.stations.keys():
            for name, st_id in self.stations.iteritems():
                if arg.lower() in name.lower():
                    station_id = st_id
                    station_name = name
                    break
        else:
            station_id = self.stations[arg]

        num_tracks = 200
        tracks = list()
        if station_id:
            try:
                tracks = self.__gmusic.get_station_tracks(station_id, \
                                                          num_tracks)
            except KeyError:
                raise RuntimeError("Operation requires an "
                                   "Unlimited subscription.")
            tracks_added = self.__enqueue_tracks(tracks)
            if tracks_added:
                if arg != station_name:
                    print_wrn("[Google Play Music] '{0}' not found. " \
                              "Playing '{1}' instead." \
                              .format(arg.encode('utf-8'), name.encode('utf-8')))
                logging.info("Added %d tracks from %s to queue", tracks_added, arg)
                self.__update_play_queue_order()
            else:
                print_wrn("[Google Play Music] '{0}' has no tracks. " \
                          .format(station_name))

        if not len(self.queue):
            print_wrn("[Google Play Music] '{0}' " \
                      "not found in the user's library. " \
                      .format(arg.encode('utf-8')))

    def __enqueue_station_unlimited(self, arg, max_results=200, quiet=False):
        """Search for a station and enqueue all of its tracks (Unlimited)

        """
        if not quiet:
            print_msg("[Google Play Music] [Station search in "\
                      "Google Play Music] : '{0}'. " \
                      .format(arg.encode('utf-8')))
        try:
            station_name = arg
            station_id = None
            station = self.__gmusic_search(arg, 'station', max_results, quiet)

            if station:
                station = station['station']
                station_name = station['name']
                seed = station['seed']
                seed_type = seed['seedType']
                track_id = seed['trackId'] if seed_type == u'2' else None
                artist_id = seed['artistId'] if seed_type == u'3' else None
                album_id = seed['albumId'] if seed_type == u'4' else None
                genre_id = seed['genreId'] if seed_type == u'5' else None
                playlist_token = seed['playlistShareToken'] if seed_type == u'8' else None
                curated_station_id = seed['curatedStationId'] if seed_type == u'9' else None
                num_tracks = max_results
                tracks = list()
                try:
                    station_id \
                        = self.__gmusic.create_station(station_name, \
                                                       track_id, \
                                                       artist_id, \
                                                       album_id, \
                                                       genre_id, \
                                                       playlist_token, \
                                                       curated_station_id)
                    tracks \
                        = self.__gmusic.get_station_tracks(station_id, \
                                                           num_tracks)
                except KeyError:
                    raise RuntimeError("Operation requires an "
                                       "Unlimited subscription.")
                tracks_added = self.__enqueue_tracks(tracks)
                if tracks_added:
                    if not quiet:
                        print_wrn("[Google Play Music] [Station] : '{0}'." \
                                  .format(station_name.encode('utf-8')))
                    logging.info("Added %d tracks from %s to queue", \
                                 tracks_added, arg.encode('utf-8'))
                    self.__update_play_queue_order()

        except KeyError:
            raise KeyError("Station not found : {0}".format(arg))

    def __enqueue_situation_unlimited(self, arg):
        """Search for a situation and enqueue all of its tracks (Unlimited)

        """
        print_msg("[Google Play Music] [Situation search in "\
                  "Google Play Music] : '{0}'. " \
                  .format(arg.encode('utf-8')))
        try:
            situation_hits = self.__gmusic.search(arg)['situation_hits']

            if not len(situation_hits):
                # Do another search with an empty string
                situation_hits = self.__gmusic.search("")['situation_hits']
                print_wrn("[Google Play Music] '{0}' not found. "\
                          "Feeling lucky?." \
                          .format(arg.encode('utf-8')))

            situation = next((hit for hit in situation_hits \
                              if 'best_result' in hit.keys()), None)

            num_tracks = 200
            if not situation and len(situation_hits):
                max_results = num_tracks / len(situation_hits)
                for hit in situation_hits:
                    situation = hit['situation']
                    print_nfo("[Google Play Music] [Situation] '{0} : {1}'." \
                              .format((hit['situation']['title']).encode('utf-8'),
                                      (hit['situation']['description']).encode('utf-8')))

                    self.__enqueue_station_unlimited(situation['title'], max_results, True)

            if not situation:
                raise KeyError

        except KeyError:
            raise KeyError("Situation not found : {0}".format(arg))

    def __enqueue_tracks(self, tracks):
        """ Add tracks to the playback queue

        """
        count = 0
        for track in tracks:
            if u'id' not in track.keys():
                track[u'id'] = track['nid']
            self.queue.append(track)
            count += 1
        return count

    def __update_playlists(self):
        """ Retrieve the user's playlists

        """
        plists = self.__gmusic.get_all_user_playlist_contents()
        for plist in plists:
            plist_name = plist['name']
            logging.info("playlist name : %s", to_ascii(plist_name))
            tracks = plist['tracks']
            tracks.sort(key=itemgetter('creationTimestamp'))
            self.playlists[plist_name] = list()
            for track in tracks:
                try:
                    song = self.song_map[track['trackId']]
                    self.playlists[plist_name].append(song)
                except IndexError:
                    pass

    def __update_playlists_unlimited(self):
        """ Retrieve shared playlists (Unlimited)

        """
        plists_subscribed_to = [p for p in self.__gmusic.get_all_playlists() \
                                if p.get('type') == 'SHARED']
        for plist in plists_subscribed_to:
            share_tok = plist['shareToken']
            playlist_items \
                = self.__gmusic.get_shared_playlist_contents(share_tok)
            plist_name = plist['name']
            logging.info("shared playlist name : %s", to_ascii(plist_name))
            self.playlists[plist_name] = list()
            for item in playlist_items:
                try:
                    song = item['track']
                    song['id'] = item['trackId']
                    self.playlists[plist_name].append(song)
                except IndexError:
                    pass

    def __gmusic_search(self, query, query_type, max_results=200, quiet=False):
        """ Search Google Play (Unlimited)

        """

        search_results = self.__gmusic.search(query, max_results)[query_type + '_hits']
        result = next((hit for hit in search_results \
                            if 'best_result' in hit.keys()), None)

        if not result and len(search_results):
            secondary_hit = None
            for hit in search_results:
                if not quiet:
                    print_nfo("[Google Play Music] [{0}] '{1}'." \
                              .format(query_type.capitalize(),
                                      (hit[query_type]['name']).encode('utf-8')))
                if query.lower() == \
                   to_ascii(hit[query_type]['name']).lower():
                    result = hit
                    break
                if query.lower() in \
                   to_ascii(hit[query_type]['name']).lower():
                    secondary_hit = hit
            if not result and secondary_hit:
                result = secondary_hit

        if not result and not len(search_results):
            # Do another search with an empty string
            search_results = self.__gmusic.search("")[query_type + '_hits']

        if not result and len(search_results):
            # Play some random result from the search results
            random.seed()
            result = random.choice(search_results)
            if not quiet:
                print_wrn("[Google Play Music] '{0}' not found. "\
                          "Feeling lucky?." \
                          .format(query.encode('utf-8')))

        return result
Example #14
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 #15
0
from gmusicapi import Mobileclient

# google play musicのプレイリストを順に削除

mc = Mobileclient()
mc.oauth_login(mc.FROM_MAC_ADDRESS)

# google play musicのプレイリストを取得
gPlaylists = mc.get_all_playlists()

for gPlaylist in gPlaylists:
    mc.delete_playlist(gPlaylist['id'])
    print("deleted playlistName = {p}".format(p=gPlaylist['name']))
Example #16
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 #17
0
class GmusicStore(BackendStore):

    # this *must* be set. Because the (most used) MediaServer Coherence also
    # allows other kind of Backends (like remote lights).
    implements = ['MediaServer']

    def __init__(self, server, *args, **kwargs):
        # first we initialize our heritage
        BackendStore.__init__(self, server, **kwargs)

        reload(sys)
        sys.setdefaultencoding('utf-8')

        # When a Backend is initialized, the configuration is given as keyword
        # arguments to the initialization. We receive it here as a dictionary
        # and allow some values to be set:

        # the name of the MediaServer as it appears in the network
        self.name = kwargs.get('name', 'Google Music')

        # timeout between updates in hours:
        self.refresh = int(kwargs.get('refresh', 1)) * (60 * 60)

        # the UPnP device that's hosting that backend, that's already done
        # in the BackendStore.__init__, just left here the sake of completeness
        self.server = server

        # initialize our containers
        self.container = GmusicContainer(None, ROOT_ID, "Google Music")
        self.container.tracks = GmusicContainer(self.container, TRACKS_ID, "Tracks")
        self.container.albums = GmusicContainer(self.container, ALBUM_ID, "Albums")
        self.container.playlists = GmusicContainer(self.container, PLAYLIST_ID, "Playlists")

        self.container.children.append(self.container.tracks)
        self.container.children.append(self.container.albums)
        self.container.children.append(self.container.playlists)

        # but as we also have to return them on 'get_by_id', we have our local
        # store of items per id:
        self.tracks = {}
        self.albums = {}
        self.artists = {}
        self.playlists = {}

        # we tell that if an XBox sends a request for images we'll
        # map the WMC id of that request to our local one
        # Todo: What the hell is going on here?
        # self.wmc_mapping = {'16': 0}

        self.username = kwargs.get('username', '')
        self.password = kwargs.get('password', '')
        self.device_id = kwargs.get('device_id', '')

        self.api = Mobileclient()

        if not self.login():
            self.info("Could not login")
            return

        # and trigger an update of the data
        dfr = self.update_data()

        # So, even though the initialize is kind of done, Coherence does not yet
        # announce our Media Server.
        # Coherence does wait for signal send by us that we are ready now.
        # And we don't want that to happen as long as we don't have succeeded
        # in fetching some first data, so we delay this signaling after the update is done:
        dfr.addCallback(self.init_completed)
        dfr.addCallback(self.queue_update)

    def login(self):
        return self.api.login(self.username, self.password, self.device_id)

    def get_by_id(self, id):
        print("asked for", id, type(id))
        # what ever we are asked for, we want to return the container only
        if isinstance(id, basestring):
            id = id.split('@', 1)
            id = id[0]
        if id == str(ROOT_ID):
            return self.container
        if id == str(ALBUM_ID):
            return self.container.albums
        if id == str(TRACKS_ID):
            return self.container.tracks
        if id == str(PLAYLIST_ID):
            return self.container.playlists

        if id in self.albums:
            self.info("id in albums:", id)
            album = self.albums.get(id, None)
            return album

        if id in self.playlists:
            self.info("id in playlists:", id)
            playlist = self.playlists.get(id, None)
            return playlist

        return self.tracks.get(id, None)

    def upnp_init(self):
        # after the signal was triggered, this method is called by coherence and

        # from now on self.server is existing and we can do
        # the necessary setup here

        # that allows us to specify our server options in more detail.

        # here we define what kind of media content we do provide
        # mostly needed to make some naughty DLNA devices behave
        # will probably move into Coherence internals one day
        self.server.connection_manager_server.set_variable(0, 'SourceProtocolInfo',
                                                           ['http-get:*:audio/mpeg:*',
                                                            'http-get:*:application/ogg:*', ]
                                                           )

        # and as it was done after we fetched the data the first time
        # we want to take care about the server wide updates as well
        self._update_container()

    def _update_container(self, result=None):
        # we need to inform Coherence about these changes
        # again this is something that will probably move
        # into Coherence internals one day
        if self.server:
            self.server.content_directory_server.set_variable(0,
                    'SystemUpdateID', self.update_id)
            value = (ROOT_ID, self.container.update_id)
            self.server.content_directory_server.set_variable(0,
                    'ContainerUpdateIDs', value)
        return result

    def update_loop(self):
        # in the loop we want to call update_data
        dfr = self.update_data()
        # after it was done we want to take care of updating
        # the container
        dfr.addCallback(self._update_container)
        # in ANY case queue an update of the data
        dfr.addBoth(self.queue_update)
        # finally clean the tempfiles
        print("Cleaning tempfiles")
        for file_id, file_tmp in cache.iteritems():
            if file_id not in recent_cache:
                os.close(file_tmp[0])
                os.remove(file_tmp[1])
        cache.clear()
        cache.update(recent_cache)
        recent_cache.clear()

    def get_data(self):
        subscribed_to_playlists = [p for p in self.api.get_all_playlists() if p.get('type') == 'SHARED']
        for playlist in subscribed_to_playlists:
            playlist["tracks"] = self.api.get_shared_playlist_contents(playlist.get("shareToken", ""))
        return (self.api.get_all_songs(), self.api.get_all_user_playlist_contents(), subscribed_to_playlists)

    def update_data(self):
        # trigger an update of the data
        self.info("Updating data")
        dfr = task.deferLater(reactor, 0, self.get_data)
        # then parse the data into our models
        dfr.addCallback(self.parse_data)
        # self.parse_data(dfr)
        self.info("Finished update")
        return dfr

    def parse_library(self, songs):
        for song in songs:
            try:
                song_id = song.get("storeId", song.get("nid", 0))
                title = song.get("title", "")
                artist_id = song.get("artistId", [0])[0]
                album_id = song.get("albumId", 0)
                album_name = song.get("album", "")
                duration = song.get("durationMillis", 0)
                album_art_uri = song.get("albumArtRef", [{"url":""}])[0].get("url", "")
                track_no = song.get("trackNumber", "0")
                disc_no = song.get("discNumber", "0")
                artist = song.get("artist", "")
                album_artist = song.get("albumArtist", artist)
                if album_id in self.albums:
                    album = self.albums[album_id]
                else:
                    album = GmusicAlbum(ALBUM_ID, self, album_id, album_name, artist_id, album_artist, album_art_uri)
                    self.container.albums.children.append(album)
                    self.albums[album_id] = album

                track = GmusicTrack(TRACKS_ID, self, song_id, title, artist, album_name, artist_id, album_id, track_no,
                                    duration, album_art_uri)

                album.tracks[int(str(disc_no) + str('{:0>10}'.format(track_no)))] = track
                self.container.tracks.children.append(track)
                self.tracks[song_id] = track
                # i = i + 1
            except Exception as e:
                print(e)
                print(song)
                traceback.print_exc()
        try:
            self.container.albums.children.sort(key=lambda x: (x.artist, x.title))
        except Exception as e:
            print("Failed to sort albums")
            print(e)
            traceback.print_exc()

    def parse_playlist_tracks(self, tracks):
        tracklist = []
        for track in tracks:
            try:
                song = track.get("track", {})
                song_id = song.get("storeId", song.get("nid", 0))
                if song_id in self.tracks:
                    tracklist.append(self.tracks[song_id])
                else:
                    title = song.get("title", "")
                    artist_id = song.get("artistId", [0])[0]
                    album_id = song.get("albumId", 0)
                    album_name = song.get("album", "")
                    duration = song.get("durationMillis", 0)
                    album_art_uri = song.get("albumArtRef", [{"url":""}])[0].get("url", "")
                    track_no = song.get("trackNumber", "0")
                    artist = song.get("artist", "")

                    track_container = GmusicTrack(TRACKS_ID, self, song_id, title, artist, album_name, artist_id, album_id, track_no,
                                        duration, album_art_uri)
                    tracklist.append(track_container)
                    self.tracks[song_id] = track_container
            except Exception as e:
                print(e)
                print(track)
                traceback.print_exc()

        return tracklist

    def parse_playlists(self, playlists):
        for playlist in playlists:
            try:
                playlist_id = playlist.get("id", playlist.get("shareToken", "DEBUG: NO_ID_TOKEN"))
                playlist_owner = playlist.get("ownerName", "DEBUG: NO_AUTHOR")
                playlist_owner_image = playlist.get("ownerProfilePhotoUrl", "DEBUG: NO_AUTHOR_IMAGE")
                playlist_name = playlist.get("name", "DEBUG: NO_NAME")
                playlist_tracks = playlist.get("tracks", {})

                playlist_container = GmusicPlaylist(PLAYLIST_ID, self, playlist_id, playlist_name, playlist_owner, playlist_owner_image)
                self.container.playlists.children.append(playlist_container)
                self.playlists[playlist_id] = playlist_container

                playlist_container.tracks = self.parse_playlist_tracks(playlist_tracks)

            except Exception as e:
                print(e)
                print(playlist)
                traceback.print_exc()

    def parse_data(self, data):
        self.info("Parsing data")
        # reset the storage
        self.container.tracks.children = []
        self.container.albums.children = []
        self.container.playlists.children = []
        self.tracks = {}
        self.albums = {}
        self.playlists = {}

        self.parse_library(data[0])
        self.parse_playlists(data[1])
        self.parse_playlists(data[2])

        self.info("Finished parsing")

        # and increase the container update id and the system update id
        # so that the clients can refresh with the new data
        # Todo: Does this actually do anyting?
        self.container.update_id += 1
        self.update_id += 1

    def queue_update(self, error_or_failure):
        # We use the reactor to queue another updating of our data
        print error_or_failure
        reactor.callLater(self.refresh, self.update_loop)
Example #18
0
songs_ids = []

for line in options.file.readlines():
    line = line.strip().decode('utf-8')
    if not line:
        continue

    log.info('Searching for %s', line)

    result = cached_search(cache, api, line)
    songs = result['song_hits']

    songs = filter_songs(songs, line)

    if len(songs):
        songs_ids.append(songs[0]['track']['storeId'])
        continue

    log.debug(songs)

for pl in api.get_all_playlists():
    if pl['name'] == options.playlist:
        api.delete_playlist(pl['id'])

playlist_id = api.create_playlist(options.playlist)
try:
    api.add_songs_to_playlist(playlist_id, songs_ids)
except Exception, _:
    api.delete_playlist(playlist_id)
Example #19
0
class tizgmusicproxy(object):
    """A class for logging into a Google Play Music account and retrieving song
    URLs.

    """

    all_songs_album_title = "All Songs"
    thumbs_up_playlist_name = "Thumbs Up"

    # pylint: disable=too-many-instance-attributes,too-many-public-methods
    def __init__(self, email, password, device_id):
        self.__gmusic = Mobileclient()
        self.__email = email
        self.__device_id = device_id
        self.logged_in = False
        self.queue = list()
        self.queue_index = -1
        self.play_queue_order = list()
        self.play_modes = TizEnumeration(["NORMAL", "SHUFFLE"])
        self.current_play_mode = self.play_modes.NORMAL
        self.now_playing_song = None

        userdir = os.path.expanduser('~')
        tizconfig = os.path.join(userdir,
                                 ".config/tizonia/." + email + ".auth_token")
        auth_token = ""
        if os.path.isfile(tizconfig):
            with open(tizconfig, "r") as f:
                auth_token = pickle.load(f)
                if auth_token:
                    # 'Keep track of the auth token' workaround. See:
                    # https://github.com/diraimondo/gmusicproxy/issues/34#issuecomment-147359198
                    print_msg("[Google Play Music] [Authenticating] : " \
                              "'with cached auth token'")
                    self.__gmusic.android_id = device_id
                    self.__gmusic.session._authtoken = auth_token
                    self.__gmusic.session.is_authenticated = True
                    try:
                        self.__gmusic.get_registered_devices()
                    except CallFailure:
                        # The token has expired. Reset the client object
                        print_wrn("[Google Play Music] [Authenticating] : " \
                                  "'auth token expired'")
                        self.__gmusic = Mobileclient()
                        auth_token = ""

        if not auth_token:
            attempts = 0
            print_nfo("[Google Play Music] [Authenticating] : " \
                      "'with user credentials'")
            while not self.logged_in and attempts < 3:
                self.logged_in = self.__gmusic.login(email, password,
                                                     device_id)
                attempts += 1

            with open(tizconfig, "a+") as f:
                f.truncate()
                pickle.dump(self.__gmusic.session._authtoken, f)

        self.library = CaseInsensitiveDict()
        self.song_map = CaseInsensitiveDict()
        self.playlists = CaseInsensitiveDict()
        self.stations = CaseInsensitiveDict()

    def logout(self):
        """ Reset the session to an unauthenticated, default state.

        """
        self.__gmusic.logout()

    def set_play_mode(self, mode):
        """ Set the playback mode.

        :param mode: curren tvalid values are "NORMAL" and "SHUFFLE"

        """
        self.current_play_mode = getattr(self.play_modes, mode)
        self.__update_play_queue_order()

    def current_song_title_and_artist(self):
        """ Retrieve the current track's title and artist name.

        """
        logging.info("current_song_title_and_artist")
        song = self.now_playing_song
        if song:
            title = to_ascii(self.now_playing_song.get('title'))
            artist = to_ascii(self.now_playing_song.get('artist'))
            logging.info("Now playing %s by %s", title, artist)
            return artist, title
        else:
            return '', ''

    def current_song_album_and_duration(self):
        """ Retrieve the current track's album and duration.

        """
        logging.info("current_song_album_and_duration")
        song = self.now_playing_song
        if song:
            album = to_ascii(self.now_playing_song.get('album'))
            duration = to_ascii \
                       (self.now_playing_song.get('durationMillis'))
            logging.info("album %s duration %s", album, duration)
            return album, int(duration)
        else:
            return '', 0

    def current_track_and_album_total(self):
        """Return the current track number and the total number of tracks in the
        album, if known.

        """
        logging.info("current_track_and_album_total")
        song = self.now_playing_song
        track = 0
        total = 0
        if song:
            try:
                track = self.now_playing_song['trackNumber']
                total = self.now_playing_song['totalTrackCount']
                logging.info("track number %s total tracks %s", track, total)
            except KeyError:
                logging.info("trackNumber or totalTrackCount : not found")
        else:
            logging.info("current_song_track_number_"
                         "and_total_tracks : not found")
        return track, total

    def current_song_year(self):
        """ Return the current track's year of publication.

        """
        logging.info("current_song_year")
        song = self.now_playing_song
        year = 0
        if song:
            try:
                year = song['year']
                logging.info("track year %s", year)
            except KeyError:
                logging.info("year : not found")
        else:
            logging.info("current_song_year : not found")
        return year

    def clear_queue(self):
        """ Clears the playback queue.

        """
        self.queue = list()
        self.queue_index = -1

    def enqueue_tracks(self, arg):
        """ Search the user's library for tracks and add
        them to the playback queue.

        :param arg: a track search term
        """
        try:
            songs = self.__gmusic.get_all_songs()

            track_hits = list()
            for song in songs:
                song_title = song['title']
                if arg.lower() in song_title.lower():
                    track_hits.append(song)
                    print_nfo("[Google Play Music] [Track] '{0}'." \
                              .format(to_ascii(song_title)))

            if not len(track_hits):
                print_wrn("[Google Play Music] '{0}' not found. "\
                          "Feeling lucky?." \
                          .format(arg.encode('utf-8')))
                random.seed()
                track_hits = random.sample(songs, MAX_TRACKS)
                for hit in track_hits:
                    song_title = hit['title']
                    print_nfo("[Google Play Music] [Track] '{0}'." \
                              .format(to_ascii(song_title)))

            if not len(track_hits):
                raise KeyError

            tracks_added = self.__enqueue_tracks(track_hits)
            logging.info("Added %d tracks from %s to queue", \
                         tracks_added, arg)

            self.__update_play_queue_order()

        except KeyError:
            raise KeyError("Track not found : {0}".format(arg))

    def enqueue_artist(self, arg):
        """ Search the user's library for tracks from the given artist and add
        them to the playback queue.

        :param arg: an artist
        """
        try:
            self.__update_local_library()
            artist = None
            artist_dict = None
            if arg not in self.library.keys():
                for name, art in self.library.iteritems():
                    if arg.lower() in name.lower():
                        artist = name
                        artist_dict = art
                        if arg.lower() != name.lower():
                            print_wrn("[Google Play Music] '{0}' not found. " \
                                      "Playing '{1}' instead." \
                                      .format(arg.encode('utf-8'), \
                                              name.encode('utf-8')))
                        break
                if not artist:
                    # Play some random artist from the library
                    random.seed()
                    artist = random.choice(self.library.keys())
                    artist_dict = self.library[artist]
                    print_wrn("[Google Play Music] '{0}' not found. "\
                              "Feeling lucky?." \
                              .format(arg.encode('utf-8')))
            else:
                artist = arg
                artist_dict = self.library[arg]
            tracks_added = 0
            for album in artist_dict:
                tracks_added += self.__enqueue_tracks(artist_dict[album])
            print_wrn("[Google Play Music] Playing '{0}'." \
                      .format(to_ascii(artist)))
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Artist not found : {0}".format(arg))

    def enqueue_album(self, arg):
        """ Search the user's library for albums with a given name and add
        them to the playback queue.

        """
        try:
            self.__update_local_library()
            album = None
            artist = None
            tentative_album = None
            tentative_artist = None
            for library_artist in self.library:
                for artist_album in self.library[library_artist]:
                    print_nfo("[Google Play Music] [Album] '{0}'." \
                              .format(to_ascii(artist_album)))
                    if not album:
                        if arg.lower() == artist_album.lower():
                            album = artist_album
                            artist = library_artist
                            break
                    if not tentative_album:
                        if arg.lower() in artist_album.lower():
                            tentative_album = artist_album
                            tentative_artist = library_artist
                if album:
                    break

            if not album and tentative_album:
                album = tentative_album
                artist = tentative_artist
                print_wrn("[Google Play Music] '{0}' not found. " \
                          "Playing '{1}' instead." \
                          .format(arg.encode('utf-8'), \
                          album.encode('utf-8')))
            if not album:
                # Play some random album from the library
                random.seed()
                artist = random.choice(self.library.keys())
                album = random.choice(self.library[artist].keys())
                print_wrn("[Google Play Music] '{0}' not found. "\
                          "Feeling lucky?." \
                          .format(arg.encode('utf-8')))

            if not album:
                raise KeyError("Album not found : {0}".format(arg))

            self.__enqueue_tracks(self.library[artist][album])
            print_wrn("[Google Play Music] Playing '{0}'." \
                      .format(to_ascii(album)))
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Album not found : {0}".format(arg))

    def enqueue_playlist(self, arg):
        """Search the user's library for playlists with a given name
        and add the tracks of the first match to the playback queue.

        Requires Unlimited subscription.

        """
        try:
            self.__update_local_library()
            self.__update_playlists()
            self.__update_playlists_unlimited()
            playlist = None
            playlist_name = None
            for name, plist in self.playlists.items():
                print_nfo("[Google Play Music] [Playlist] '{0}'." \
                          .format(to_ascii(name)))
            if arg not in self.playlists.keys():
                for name, plist in self.playlists.iteritems():
                    if arg.lower() in name.lower():
                        playlist = plist
                        playlist_name = name
                        if arg.lower() != name.lower():
                            print_wrn("[Google Play Music] '{0}' not found. " \
                                      "Playing '{1}' instead." \
                                      .format(arg.encode('utf-8'), \
                                              to_ascii(name)))
                            break
            else:
                playlist_name = arg
                playlist = self.playlists[arg]

            random.seed()
            x = 0
            while (not playlist or not len(playlist)) and x < 3:
                x += 1
                # Play some random playlist from the library
                playlist_name = random.choice(self.playlists.keys())
                playlist = self.playlists[playlist_name]
                print_wrn("[Google Play Music] '{0}' not found or found empty. "\
                          "Feeling lucky?." \
                          .format(arg.encode('utf-8')))

            if not len(playlist):
                raise KeyError

            self.__enqueue_tracks(playlist)
            print_wrn("[Google Play Music] Playing '{0}'." \
                      .format(to_ascii(playlist_name)))
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError(
                "Playlist not found or found empty : {0}".format(arg))

    def enqueue_podcast(self, arg):
        """Search Google Play Music for a podcast series and add its tracks to the
        playback queue ().

        Requires Unlimited subscription.

        """
        print_msg("[Google Play Music] [Retrieving podcasts] : '{0}'. " \
                  .format(self.__email))

        try:

            self.__enqueue_podcast(arg)

            if not len(self.queue):
                raise KeyError

            logging.info("Added %d episodes from '%s' to queue", \
                         len(self.queue), arg)
            self.__update_play_queue_order()

        except KeyError:
            raise KeyError("Podcast not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_station_unlimited(self, arg):
        """Search the user's library for a station with a given name
        and add its tracks to the playback queue.

        Requires Unlimited subscription.

        """
        try:
            # First try to find a suitable station in the user's library
            self.__enqueue_user_station_unlimited(arg)

            if not len(self.queue):
                # If no suitable station is found in the user's library, then
                # search google play unlimited for a potential match.
                self.__enqueue_station_unlimited(arg)

            if not len(self.queue):
                raise KeyError

        except KeyError:
            raise KeyError("Station not found : {0}".format(arg))

    def enqueue_genre_unlimited(self, arg):
        """Search Unlimited for a genre with a given name and add its
        tracks to the playback queue.

        Requires Unlimited subscription.

        """
        print_msg("[Google Play Music] [Retrieving genres] : '{0}'. " \
                  .format(self.__email))

        try:
            all_genres = list()
            root_genres = self.__gmusic.get_genres()
            second_tier_genres = list()
            for root_genre in root_genres:
                second_tier_genres += self.__gmusic.get_genres(
                    root_genre['id'])
            all_genres += root_genres
            all_genres += second_tier_genres
            for genre in all_genres:
                print_nfo("[Google Play Music] [Genre] '{0}'." \
                          .format(to_ascii(genre['name'])))
            genre = dict()
            if arg not in all_genres:
                genre = next((g for g in all_genres \
                              if arg.lower() in to_ascii(g['name']).lower()), \
                             None)

            tracks_added = 0
            while not tracks_added:
                if not genre and len(all_genres):
                    # Play some random genre from the search results
                    random.seed()
                    genre = random.choice(all_genres)
                    print_wrn("[Google Play Music] '{0}' not found. "\
                              "Feeling lucky?." \
                              .format(arg.encode('utf-8')))

                genre_name = genre['name']
                genre_id = genre['id']
                station_id = self.__gmusic.create_station(genre_name, \
                                                          None, None, None, genre_id)
                num_tracks = MAX_TRACKS
                tracks = self.__gmusic.get_station_tracks(
                    station_id, num_tracks)
                tracks_added = self.__enqueue_tracks(tracks)
                logging.info("Added %d tracks from %s to queue", tracks_added,
                             genre_name)
                if not tracks_added:
                    # This will produce another iteration in the loop
                    genre = None

            print_wrn("[Google Play Music] Playing '{0}'." \
                      .format(to_ascii(genre['name'])))
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Genre not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_situation_unlimited(self, arg):
        """Search Unlimited for a situation with a given name and add its
        tracks to the playback queue.

        Requires Unlimited subscription.

        """
        print_msg("[Google Play Music] [Retrieving situations] : '{0}'. " \
                  .format(self.__email))

        try:

            self.__enqueue_situation_unlimited(arg)

            if not len(self.queue):
                raise KeyError

            logging.info("Added %d tracks from %s to queue", \
                         len(self.queue), arg)

        except KeyError:
            raise KeyError("Situation not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_artist_unlimited(self, arg):
        """Search Unlimited for an artist and add the artist's 200 top tracks to the
        playback queue.

        Requires Unlimited subscription.

        """
        try:
            artist = self.__gmusic_search(arg, 'artist')

            include_albums = False
            max_top_tracks = MAX_TRACKS
            max_rel_artist = 0
            artist_tracks = dict()
            if artist:
                artist_tracks = self.__gmusic.get_artist_info \
                                (artist['artist']['artistId'],
                                 include_albums, max_top_tracks,
                                 max_rel_artist)['topTracks']

            if not artist_tracks:
                raise KeyError

            for track in artist_tracks:
                song_title = track['title']
                print_nfo("[Google Play Music] [Track] '{0}'." \
                          .format(to_ascii(song_title)))

            tracks_added = self.__enqueue_tracks(artist_tracks)
            logging.info("Added %d tracks from %s to queue", \
                         tracks_added, arg)
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Artist not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_album_unlimited(self, arg):
        """Search Unlimited for an album and add its tracks to the
        playback queue.

        Requires Unlimited subscription.

        """
        try:
            album = self.__gmusic_search(arg, 'album')
            album_tracks = dict()
            if album:
                album_tracks = self.__gmusic.get_album_info \
                               (album['album']['albumId'])['tracks']
            if not album_tracks:
                raise KeyError

            print_wrn("[Google Play Music] Playing '{0}'." \
                      .format((album['album']['name']).encode('utf-8')))

            tracks_added = self.__enqueue_tracks(album_tracks)
            logging.info("Added %d tracks from %s to queue", \
                         tracks_added, arg)
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Album not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_tracks_unlimited(self, arg):
        """ Search Unlimited for a track name and add all the matching tracks
        to the playback queue.

        Requires Unlimited subscription.

        """
        print_msg("[Google Play Music] [Retrieving library] : '{0}'. " \
                  .format(self.__email))

        try:
            max_results = MAX_TRACKS
            track_hits = self.__gmusic.search(arg, max_results)['song_hits']
            if not len(track_hits):
                # Do another search with an empty string
                track_hits = self.__gmusic.search("", max_results)['song_hits']
                print_wrn("[Google Play Music] '{0}' not found. "\
                          "Feeling lucky?." \
                          .format(arg.encode('utf-8')))

            tracks = list()
            for hit in track_hits:
                tracks.append(hit['track'])
            tracks_added = self.__enqueue_tracks(tracks)
            logging.info("Added %d tracks from %s to queue", \
                         tracks_added, arg)
            self.__update_play_queue_order()
        except KeyError:
            raise KeyError("Playlist not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_playlist_unlimited(self, arg):
        """Search Unlimited for a playlist name and add all its tracks to the
        playback queue.

        Requires Unlimited subscription.

        """
        print_msg("[Google Play Music] [Retrieving playlists] : '{0}'. " \
                  .format(self.__email))

        try:
            playlist_tracks = list()

            playlist_hits = self.__gmusic_search(arg, 'playlist')
            if playlist_hits:
                playlist = playlist_hits['playlist']
                playlist_contents = self.__gmusic.get_shared_playlist_contents(
                    playlist['shareToken'])
            else:
                raise KeyError

            print_nfo("[Google Play Music] [Playlist] '{}'." \
                      .format(playlist['name']).encode('utf-8'))

            for item in playlist_contents:
                print_nfo("[Google Play Music] [Playlist Track] '{} by {} (Album: {}, {})'." \
                          .format((item['track']['title']).encode('utf-8'),
                                  (item['track']['artist']).encode('utf-8'),
                                  (item['track']['album']).encode('utf-8'),
                                  (item['track']['year'])))
                track = item['track']
                playlist_tracks.append(track)

            if not playlist_tracks:
                raise KeyError

            tracks_added = self.__enqueue_tracks(playlist_tracks)
            logging.info("Added %d tracks from %s to queue", \
                         tracks_added, arg)
            self.__update_play_queue_order()

        except KeyError:
            raise KeyError("Playlist not found : {0}".format(arg))
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def enqueue_promoted_tracks_unlimited(self):
        """ Retrieve the url of the next track in the playback queue.

        """
        try:
            tracks = self.__gmusic.get_promoted_songs()
            count = 0
            for track in tracks:
                store_track = self.__gmusic.get_track_info(track['storeId'])
                if u'id' not in store_track.keys():
                    store_track[u'id'] = store_track['storeId']
                self.queue.append(store_track)
                count += 1
            if count == 0:
                print_wrn("[Google Play Music] Operation requires " \
                          "an Unlimited subscription.")
            logging.info("Added %d Unlimited promoted tracks to queue", \
                         count)
            self.__update_play_queue_order()
        except CallFailure:
            raise RuntimeError("Operation requires an Unlimited subscription.")

    def next_url(self):
        """ Retrieve the url of the next track in the playback queue.

        """
        if len(self.queue):
            self.queue_index += 1
            if (self.queue_index < len(self.queue)) \
               and (self.queue_index >= 0):
                next_song = self.queue[self.play_queue_order[self.queue_index]]
                return self.__retrieve_track_url(next_song)
            else:
                self.queue_index = -1
                return self.next_url()
        else:
            return ''

    def prev_url(self):
        """ Retrieve the url of the previous track in the playback queue.

        """
        if len(self.queue):
            self.queue_index -= 1
            if (self.queue_index < len(self.queue)) \
               and (self.queue_index >= 0):
                prev_song = self.queue[self.play_queue_order[self.queue_index]]
                return self.__retrieve_track_url(prev_song)
            else:
                self.queue_index = len(self.queue)
                return self.prev_url()
        else:
            return ''

    def __update_play_queue_order(self):
        """ Update the queue playback order.

        A sequential order is applied if the current play mode is "NORMAL" or a
        random order if current play mode is "SHUFFLE"

        """
        total_tracks = len(self.queue)
        if total_tracks:
            if not len(self.play_queue_order):
                # Create a sequential play order, if empty
                self.play_queue_order = range(total_tracks)
            if self.current_play_mode == self.play_modes.SHUFFLE:
                random.shuffle(self.play_queue_order)
            print_nfo("[Google Play Music] [Tracks in queue] '{0}'." \
                      .format(total_tracks))

    def __retrieve_track_url(self, song):
        """ Retrieve a song url

        """
        if song.get('episodeId'):
            song_url = self.__gmusic.get_podcast_episode_stream_url(
                song['episodeId'], self.__device_id)
        else:
            song_url = self.__gmusic.get_stream_url(song['id'],
                                                    self.__device_id)

        try:
            self.now_playing_song = song
            return song_url
        except AttributeError:
            logging.info("Could not retrieve the song url!")
            raise

    def __update_local_library(self):
        """ Retrieve the songs and albums from the user's library

        """
        print_msg("[Google Play Music] [Retrieving library] : '{0}'. " \
                  .format(self.__email))

        songs = self.__gmusic.get_all_songs()
        self.playlists[self.thumbs_up_playlist_name] = list()

        # Retrieve the user's song library
        for song in songs:
            if "rating" in song and song['rating'] == "5":
                self.playlists[self.thumbs_up_playlist_name].append(song)

            song_id = song['id']
            song_artist = song['artist']
            song_album = song['album']

            self.song_map[song_id] = song

            if song_artist == "":
                song_artist = "Unknown Artist"

            if song_album == "":
                song_album = "Unknown Album"

            if song_artist not in self.library:
                self.library[song_artist] = CaseInsensitiveDict()
                self.library[song_artist][self.all_songs_album_title] = list()

            if song_album not in self.library[song_artist]:
                self.library[song_artist][song_album] = list()

            self.library[song_artist][song_album].append(song)
            self.library[song_artist][self.all_songs_album_title].append(song)

        # Sort albums by track number
        for artist in self.library.keys():
            logging.info("Artist : %s", to_ascii(artist))
            for album in self.library[artist].keys():
                logging.info("   Album : %s", to_ascii(album))
                if album == self.all_songs_album_title:
                    sorted_album = sorted(self.library[artist][album],
                                          key=lambda k: k['title'])
                else:
                    sorted_album = sorted(
                        self.library[artist][album],
                        key=lambda k: k.get('trackNumber', 0))
                self.library[artist][album] = sorted_album

    def __update_stations_unlimited(self):
        """ Retrieve stations (Unlimited)

        """
        self.stations.clear()
        stations = self.__gmusic.get_all_stations()
        self.stations[u"I'm Feeling Lucky"] = 'IFL'
        for station in stations:
            station_name = station['name']
            logging.info("station name : %s", to_ascii(station_name))
            self.stations[station_name] = station['id']

    def __enqueue_user_station_unlimited(self, arg):
        """ Enqueue a user station (Unlimited)

        """
        print_msg("[Google Play Music] [Station search "\
                  "in user's library] : '{0}'. " \
                  .format(self.__email))
        self.__update_stations_unlimited()
        station_name = arg
        station_id = None
        for name, st_id in self.stations.iteritems():
            print_nfo("[Google Play Music] [Station] '{0}'." \
                      .format(to_ascii(name)))
        if arg not in self.stations.keys():
            for name, st_id in self.stations.iteritems():
                if arg.lower() in name.lower():
                    station_id = st_id
                    station_name = name
                    break
        else:
            station_id = self.stations[arg]

        num_tracks = MAX_TRACKS
        tracks = list()
        if station_id:
            try:
                tracks = self.__gmusic.get_station_tracks(station_id, \
                                                          num_tracks)
            except KeyError:
                raise RuntimeError("Operation requires an "
                                   "Unlimited subscription.")
            tracks_added = self.__enqueue_tracks(tracks)
            if tracks_added:
                if arg.lower() != station_name.lower():
                    print_wrn("[Google Play Music] '{0}' not found. " \
                              "Playing '{1}' instead." \
                              .format(arg.encode('utf-8'), name.encode('utf-8')))
                logging.info("Added %d tracks from %s to queue", tracks_added,
                             arg)
                self.__update_play_queue_order()
            else:
                print_wrn("[Google Play Music] '{0}' has no tracks. " \
                          .format(station_name))

        if not len(self.queue):
            print_wrn("[Google Play Music] '{0}' " \
                      "not found in the user's library. " \
                      .format(arg.encode('utf-8')))

    def __enqueue_station_unlimited(self,
                                    arg,
                                    max_results=MAX_TRACKS,
                                    quiet=False):
        """Search for a station and enqueue all of its tracks (Unlimited)

        """
        if not quiet:
            print_msg("[Google Play Music] [Station search in "\
                      "Google Play Music] : '{0}'. " \
                      .format(arg.encode('utf-8')))
        try:
            station_name = arg
            station_id = None
            station = self.__gmusic_search(arg, 'station', max_results, quiet)

            if station:
                station = station['station']
                station_name = station['name']
                seed = station['seed']
                seed_type = seed['seedType']
                track_id = seed['trackId'] if seed_type == u'2' else None
                artist_id = seed['artistId'] if seed_type == u'3' else None
                album_id = seed['albumId'] if seed_type == u'4' else None
                genre_id = seed['genreId'] if seed_type == u'5' else None
                playlist_token = seed[
                    'playlistShareToken'] if seed_type == u'8' else None
                curated_station_id = seed[
                    'curatedStationId'] if seed_type == u'9' else None
                num_tracks = max_results
                tracks = list()
                try:
                    station_id \
                        = self.__gmusic.create_station(station_name, \
                                                       track_id, \
                                                       artist_id, \
                                                       album_id, \
                                                       genre_id, \
                                                       playlist_token, \
                                                       curated_station_id)
                    tracks \
                        = self.__gmusic.get_station_tracks(station_id, \
                                                           num_tracks)
                except KeyError:
                    raise RuntimeError("Operation requires an "
                                       "Unlimited subscription.")
                tracks_added = self.__enqueue_tracks(tracks)
                if tracks_added:
                    if not quiet:
                        print_wrn("[Google Play Music] [Station] : '{0}'." \
                                  .format(station_name.encode('utf-8')))
                    logging.info("Added %d tracks from %s to queue", \
                                 tracks_added, arg.encode('utf-8'))
                    self.__update_play_queue_order()

        except KeyError:
            raise KeyError("Station not found : {0}".format(arg))

    def __enqueue_situation_unlimited(self, arg):
        """Search for a situation and enqueue all of its tracks (Unlimited)

        """
        print_msg("[Google Play Music] [Situation search in "\
                  "Google Play Music] : '{0}'. " \
                  .format(arg.encode('utf-8')))
        try:
            situation_hits = self.__gmusic.search(arg)['situation_hits']

            # If the search didn't return results, just do another search with
            # an empty string
            if not len(situation_hits):
                situation_hits = self.__gmusic.search("")['situation_hits']
                print_wrn("[Google Play Music] '{0}' not found. "\
                          "Feeling lucky?." \
                          .format(arg.encode('utf-8')))

            # Try to find a "best result", if one exists
            situation = next((hit for hit in situation_hits \
                              if 'best_result' in hit.keys() \
                              and hit['best_result'] == True), None)

            num_tracks = MAX_TRACKS

            # If there is no best result, then get a selection of tracks from
            # each situation. At least we'll play some music.
            if not situation and len(situation_hits):
                max_results = num_tracks / len(situation_hits)
                for hit in situation_hits:
                    situation = hit['situation']
                    print_nfo("[Google Play Music] [Situation] '{0} : {1}'." \
                              .format((hit['situation']['title']).encode('utf-8'),
                                      (hit['situation']['description']).encode('utf-8')))
                    self.__enqueue_station_unlimited(situation['title'],
                                                     max_results, True)
            elif situation:
                # There is at list one sitution, enqueue its tracks.
                situation = situation['situation']
                max_results = num_tracks
                self.__enqueue_station_unlimited(situation['title'],
                                                 max_results, True)

            if not situation:
                raise KeyError

        except KeyError:
            raise KeyError("Situation not found : {0}".format(arg))

    def __enqueue_podcast(self, arg):
        """Search for a podcast series and enqueue all of its tracks.

        """
        print_msg("[Google Play Music] [Podcast search in "\
                  "Google Play Music] : '{0}'. " \
                  .format(arg.encode('utf-8')))
        try:
            podcast_hits = self.__gmusic_search(arg,
                                                'podcast',
                                                10,
                                                quiet=False)

            if not podcast_hits:
                print_wrn(
                    "[Google Play Music] [Podcast] 'Search returned zero results'."
                )
                print_wrn(
                    "[Google Play Music] [Podcast] 'Are you in a supported region "
                    "(currently only US and Canada) ?'")

            # Use the first podcast retrieved. At least we'll play something.
            podcast = dict()
            if podcast_hits and len(podcast_hits):
                podcast = podcast_hits['series']

            episodes_added = 0
            if podcast:
                # There is a podcast, enqueue its episodes.
                print_nfo("[Google Play Music] [Podcast] 'Playing '{0}' by {1}'." \
                          .format((podcast['title']).encode('utf-8'),
                                  (podcast['author']).encode('utf-8')))
                print_nfo("[Google Play Music] [Podcast] '{0}'." \
                          .format((podcast['description'][0:150]).encode('utf-8')))
                series = self.__gmusic.get_podcast_series_info(
                    podcast['seriesId'])
                episodes = series['episodes']
                for episode in episodes:
                    print_nfo("[Google Play Music] [Podcast Episode] '{0} : {1}'." \
                              .format((episode['title']).encode('utf-8'),
                                      (episode['description'][0:80]).encode('utf-8')))
                episodes_added = self.__enqueue_tracks(episodes)

            if not podcast or not episodes_added:
                raise KeyError

        except KeyError:
            raise KeyError(
                "Podcast not found or no episodes found: {0}".format(arg))

    def __enqueue_tracks(self, tracks):
        """ Add tracks to the playback queue

        """
        count = 0
        for track in tracks:
            if u'id' not in track.keys() and track.get('storeId'):
                track[u'id'] = track['storeId']
            self.queue.append(track)
            count += 1
        return count

    def __update_playlists(self):
        """ Retrieve the user's playlists

        """
        plists = self.__gmusic.get_all_user_playlist_contents()
        for plist in plists:
            plist_name = plist.get('name')
            tracks = plist.get('tracks')
            if plist_name and tracks:
                logging.info("playlist name : %s", to_ascii(plist_name))
                tracks.sort(key=itemgetter('creationTimestamp'))
                self.playlists[plist_name] = list()
                for track in tracks:
                    song_id = track.get('trackId')
                    if song_id:
                        song = self.song_map.get(song_id)
                        if song:
                            self.playlists[plist_name].append(song)

    def __update_playlists_unlimited(self):
        """ Retrieve shared playlists (Unlimited)

        """
        plists_subscribed_to = [p for p in self.__gmusic.get_all_playlists() \
                                if p.get('type') == 'SHARED']
        for plist in plists_subscribed_to:
            share_tok = plist['shareToken']
            playlist_items \
                = self.__gmusic.get_shared_playlist_contents(share_tok)
            plist_name = plist['name']
            logging.info("shared playlist name : %s", to_ascii(plist_name))
            self.playlists[plist_name] = list()
            for item in playlist_items:
                try:
                    song = item['track']
                    song['id'] = item['trackId']
                    self.playlists[plist_name].append(song)
                except IndexError:
                    pass

    def __gmusic_search(self,
                        query,
                        query_type,
                        max_results=MAX_TRACKS,
                        quiet=False):
        """ Search Google Play (Unlimited)

        """

        search_results = self.__gmusic.search(query, max_results)[query_type +
                                                                  '_hits']

        # This is a workaround. Some podcast results come without these two
        # keys in the dictionary
        if query_type == "podcast" and len(search_results) \
           and not search_results[0].get('navigational_result'):
            for res in search_results:
                res[u'best_result'] = False
                res[u'navigational_result'] = False
                res[query_type] = res['series']

        result = ''
        if query_type != "playlist":
            result = next((hit for hit in search_results \
                           if 'best_result' in hit.keys() \
                           and hit['best_result'] == True), None)

        if not result and len(search_results):
            secondary_hit = None
            for hit in search_results:
                name = ''
                if hit[query_type].get('name'):
                    name = hit[query_type].get('name')
                elif hit[query_type].get('title'):
                    name = hit[query_type].get('title')
                if not quiet:
                    print_nfo("[Google Play Music] [{0}] '{1}'." \
                              .format(query_type.capitalize(),
                                      (name).encode('utf-8')))
                if query.lower() == \
                   to_ascii(name).lower():
                    result = hit
                    break
                if query.lower() in \
                   to_ascii(name).lower():
                    secondary_hit = hit
            if not result and secondary_hit:
                result = secondary_hit

        if not result and not len(search_results):
            # Do another search with an empty string
            search_results = self.__gmusic.search("")[query_type + '_hits']

        if not result and len(search_results):
            # Play some random result from the search results
            random.seed()
            result = random.choice(search_results)
            if not quiet:
                print_wrn("[Google Play Music] '{0}' not found. "\
                          "Feeling lucky?." \
                          .format(query.encode('utf-8')))

        return result
Example #20
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)
Example #21
0
class GPMPlGen:
    def __init__(self, config):
        self.config = config

        logging.basicConfig(level=config.log_level)
        self.logger = logging.getLogger(__name__)

        # Client
        self.client = Mobileclient(debug_logging=False)
        gmusicapi_logger = logging.getLogger('gmusicapi.utils')
        gmusicapi_logger.setLevel(logging.INFO)

        # Logging in
        self.logger.info('Logging in...')
        try:
            android_id = config.client_id
            if android_id is None:
                android_id = Mobileclient.FROM_MAC_ADDRESS
            success = self.client.oauth_login(android_id)
            if not success:
                self.client.perform_oauth()
                success = self.client.oauth_login(android_id)
                if not success:
                    raise GPMPlGenException("Could not log in")
        except Exception as e:
            raise GPMPlGenException("Could not log in", e)

        # Setting up writer
        if config.dry_run:
            self.logger.info('Running in DRY-RUN mode; setting up mock client...')
            self.writer_client = ClientMock()
        else:
            self.writer_client = self.client

        # Local database
        self.db = LibraryDb(config.local_db)

        # Internal stuff
        self.timestamp = time.time()

    def _get_all_songs(self):
        self.logger.info("Downloading all tracks from library")
        try:
            library_from_gpm = self.client.get_all_songs(incremental=False)
        except Exception as e:
            raise GPMPlGenException("Could not download library", e)
        self.logger.info("Loaded %d songs" % (len(library_from_gpm)))
        return library_from_gpm

    def _get_all_playlists_songs(self, static_playlists):
        self.logger.info("Downloading all tracks from playlists")
        try:
            playlists_with_contents = self.client.get_all_user_playlist_contents()
            self.logger.info("Loaded %d playlists" % (len(playlists_with_contents)))
        except Exception as e:
            raise GPMPlGenException("Could not download playlist contents", e)
        songs_from_static_playlists = []
        static_playlists_ids = map((lambda p: p['id']), static_playlists)
        for p in playlists_with_contents:
            if p['id'] in static_playlists_ids:
                if 'tracks' in p:
                    for t in p['tracks']:
                        if 'track' in t:
                            track = t['track']
                            track['id'] = t['id']
                        else:
                            track = t
                        songs_from_static_playlists.append(track)
        self.logger.info("Loaded %d songs" % (len(songs_from_static_playlists)))
        return songs_from_static_playlists

    def store_songs_in_db(self):
        library_from_gpm = self._get_all_songs()
        self.db.ingest_track_list(library_from_gpm, LibraryDb.LIBRARY_TABLE)

    def store_playlists_in_db(self, get_songs=True):
        self.logger.info("Downloading all generated playlists from library")
        (generated_playlists, other_playlists) = self._get_all_playlists()
        self.logger.info("Loading %d generated playlists"
                         % (len(generated_playlists)))
        self.db.ingest_generated_playlists(generated_playlists)
        if not get_songs:
            return
        self.logger.info("Loading %d static playlists"
                         % (len(other_playlists)))
        tracks = self._get_all_playlists_songs(other_playlists)
        self.db.ingest_track_list(tracks, LibraryDb.STATIC_PL_TABLE)

    def retrieve_library(self, get_songs=True):
        if self.db.cache_mode:
            if self.config.write_to_db:
                self.logger.info("Storing into cache")
                self.store_songs_in_db()
                self.store_playlists_in_db()
                self.db.consolidate_all_tracks()
            else:
                self.logger.info("Using cache")
            return

        # Get songs
        if get_songs:
            self.store_songs_in_db()

        # Get playlists and potentially their contents
        self.store_playlists_in_db(get_songs)

        # Generate full track list
        if get_songs:
            self.db.consolidate_all_tracks()

    def _get_all_playlists(self):
        self.logger.info('Getting playlists')
        playlists_from_gpm = []
        try:
            playlists_from_gpm = self.client.get_all_playlists()
        except Exception as e:
            raise GPMPlGenException("Could not download playlists", e)
        generated_playlists = []
        static_playlists = []
        for pl in playlists_from_gpm:
            if Playlist.is_generated_by_gpmplgen(pl):
                generated_playlists.append(pl)
            else:
                static_playlists.append(pl)
        return (generated_playlists, static_playlists)

    def delete_playlists(self, playlists):
        # FIXME: replace with playlist.delete
        for pl in playlists:
            self.logger.info('Deleting %s: %s' % (pl.id, pl.name))
            self.writer_client.delete_playlist(pl.id)

    def cleanup_all_generated_playlists(self):
        self.delete_playlists(self.db.get_generated_playlists())

    def generate_playlist(self, playlist_type, playlistConfig):
        generator = PlaylistGenerator(self.config.playlist_prefix, self.timestamp, self.db)
        try:
            generator_results = generator.generate(playlist_type, playlistConfig)
        except PlaylistGeneratorError as e:
            self.logger.error(e)
            return
        self.delete_playlists(generator_results.get_playlists_to_delete())
        playlists_to_create = generator_results.get_playlists_to_create()
        created = 0
        try:
            for pl in playlists_to_create:
                pl.create_in_gpm(self.writer_client)
                created = created + 1
        except GPMPlGenException as e:
            self.logger.error("Error talking to Google Play Music; attempting to clean-up")
            for pl in playlists_to_create:
                pl.delete_in_gpm(self.writer_client)
            self.logger.debug(e.parent_exception)
            raise e  # FIXME: maybe not?
        return created
    # GPLAY_USERNAME= (no quotes)
    # GPLAY_PW= (no quotes)
    load_dotenv(find_dotenv())

    # Set up Google Play Music Mobile Client
    mc = Mobileclient()

    # Retrieve Google Play Music email and password from environment variables
    gplay_email = os.environ['GPLAY_USERNAME']
    gplay_password = os.environ['GPLAY_PW']

    logged_in = mc.login(gplay_email, gplay_password, Mobileclient.FROM_MAC_ADDRESS)

    if logged_in:
        # Save list of current playlists to not add duplicates
        current_playlists = [d['name'] for d in mc.get_all_playlists()]

        if len(sys.argv) > 1:
            username = sys.argv[1]
        else:
            print "usage: python music_conversion.py [spotify_username]"
            sys.exit()
        scope = 'user-library-read'
        token = util.prompt_for_user_token(username, scope)

        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
Example #23
0
from cmenu import Menu

api = Mobileclient(debug_logging = False)

if not args.username:
 args.username = raw_input('Username: '******'Password:'******'Logging in uwer %s.', args.username)
if not api.login(args.username, args.password, ''.join([choice('1234567890abcdef') for x in xrange(16)]) if args.random_id else api.FROM_MAC_ADDRESS):
 quit('Login failed.')

m = Menu('Select source to download')
m.add_entry('Library', api.get_all_songs)
for p in api.get_all_playlists():
 m.add_entry('%s playlist' % p.get('name', 'Untitled'), lambda token = p['shareToken']: [x['track'] for x in api.get_shared_playlist_contents(token)])
res = m.get_selection()
if res:
 logging.debug('Getting source contents.')
 program_start = time()
 logging.info('Started the download at %s.', ctime(program_start))
 total = 0.0 # The average download time.
 try:
  for i, r in enumerate(res()):
   logging.debug('Result = %s', r)
   artist = valid_filename(r.get('artist', 'Unknown Artist'))
   album = valid_filename(r.get('album', 'Unknown Album'))
   number = r.get('trackNumber', 0)
   if number < 10:
    number = '0%s' % number
Example #24
0
def main():
    global player
    global api

    parser = OptionParser()
    parser.add_option("-p", "--playlist", dest="playlist", help="Playlist (Name or ID)")
    parser.add_option("-t", "--track", dest="track", help="Track (Name or ID)")
    parser.add_option("-l", "--listen", action="store_true", dest="listen", help="Start listening")
    parser.add_option("-c", "--continue", action="store_true", dest="cont", help="Continue playlist after track")
    parser.add_option("-s", "--shuffle", action="store_true", dest="shuffle", help="Randomize playlist")

    (opts, args) = parser.parse_args()

    config = ConfigParser.RawConfigParser()
    directory = os.path.dirname(os.path.realpath(sys.argv[0]))
    config.read(directory + '/.gmusicpy')
    username = config.get('gmusic', 'username')
    password = config.get('gmusic', 'password')

    api = Mobileclient()
    api.login(username, password, Mobileclient.FROM_MAC_ADDRESS)

    if not api.is_authenticated():
        print "Bad username/password"
        return

    id = 0

    try:
        if(opts.playlist):
            playlists = api.get_all_user_playlist_contents()
            playlist = findPlaylist(opts.playlist, playlists)
            
            if(playlist is None):
                print 'Playlist not found'
                return

            if(opts.track):
                item = findTrack(opts.track, playlist)

                if(item is None):
                    print 'Track not found'
                    return

                track = item['track']
                track['trackId'] = item['trackId']
                tracklist.append(track)

            else:
                for item in playlist['tracks']:
                    track = item['track']
                    track['trackId'] = item['trackId']
                    tracklist.append(track)

            if(opts.shuffle):
                shuffle(tracklist)

            if(opts.listen):
                track = tracklist.pop(0)
                printTrack("", track)
                url = api.get_stream_url(track['trackId'])
                player = play(url)
            else:
                for track in tracklist:
                    printTrack(id, track)
                    id = id + 1

        else:
            playlists = api.get_all_playlists()
            for playlist in playlists:
                print str(id) + ' ' + playlist['name']
                id = id + 1

        while(True):
            if player == None:
                break
            if isinstance(player, subprocess.Popen) and player.poll() != None:
                if(len(tracklist) > 0):
                    track = tracklist.pop(0)
                    printTrack("", track)
                    url = api.get_stream_url(track['trackId'])
                    player = play(url)
                else:
                    break;
            sleep(0.2)

    finally:
        if isinstance(player, subprocess.Popen):
            player.terminate()
        api.logout()
    # Need to get a valid device ID from the exception text when using an invalid ID.
    deviceId = 'deviceId'
    while True:
        try:
            mc.oauth_login(deviceId)
            break
        except InvalidDeviceId as exc:
            deviceId = str(exc).split('\n')[1].replace('* ', '')
else:
    # First time login - do OAuth in browser
    mc.perform_oauth(open_browser=True)

# Find the playlist if it exists, or create it
print('Finding playlist "{0}"'.format(playlistName))
existingPlaylist = list(
    filter(lambda p: p['name'] == playlistName, mc.get_all_playlists()))
if len(existingPlaylist) > 0:
    playlistId = existingPlaylist[0]['id']
else:
    playlistId = mc.create_playlist(playlistName)

print('Fetching data from server')

# Get all songs in library
allTracks = mc.get_all_songs()

# Get contents of our playlist
playlist = list(
    filter(lambda p: p['id'] == playlistId,
           mc.get_all_user_playlist_contents()))[0]
Example #26
0
class Session(object):
    def __init__(self):
        self.api = None
        self.user = None
        self.lib_albums = {}
        self.lib_artists = {}
        self.lib_tracks = {}
        self.lib_updatetime = 0
        self.sitdata = []
        self.sitbyid = {}
        self.sitdataupdtime = 0

    def dmpdata(self, who, data):
        print("%s: %s" % (who, json.dumps(data, indent=4)), file=sys.stderr)

    def login(self, username, password, deviceid=None):
        self.api = Mobileclient(debug_logging=False)

        if deviceid is None:
            logged_in = self.api.login(username, password, Mobileclient.FROM_MAC_ADDRESS)
        else:
            logged_in = self.api.login(username, password, deviceid)

        # print("Logged in: %s" % logged_in)
        # data = self.api.get_registered_devices()
        # print("registered: %s" % data)
        # isauth = self.api.is_authenticated()
        # print("Auth ok: %s" % isauth)
        return logged_in

    def _get_user_library(self):
        now = time.time()
        if now - self.lib_updatetime < 300:
            return
        data = self.api.get_all_songs()
        # self.dmpdata("all_songs", data)
        self.lib_updatetime = now
        tracks = [_parse_track(t) for t in data]
        self.lib_tracks = dict([(t.id, t) for t in tracks])
        for track in tracks:
            # We would like to use the album id here, but gmusic
            # associates the tracks with any compilations after
            # uploading (does not use the metadata apparently), so
            # that we can't (we would end up with multiple
            # albums). OTOH, the album name is correct (so seems to
            # come from the metadata). What we should do is test the
            # album ids for one album with a matching title, but we're
            # not sure to succeed. So at this point, the album id we
            # end up storing could be for a different albums, and we
            # should have a special library-local get_album_tracks
            self.lib_albums[track.album.name] = track.album
            self.lib_artists[track.artist.id] = track.artist

    def get_user_albums(self):
        self._get_user_library()
        return self.lib_albums.values()

    def get_user_artists(self):
        self._get_user_library()
        return self.lib_artists.values()

    def get_user_playlists(self):
        pldata = self.api.get_all_playlists()
        # self.dmpdata("playlists", pldata)
        return [_parse_playlist(pl) for pl in pldata]

    def get_user_playlist_tracks(self, playlist_id):
        self._get_user_library()
        data = self.api.get_all_user_playlist_contents()
        # self.dmpdata("user_playlist_content", data)
        trkl = [item["tracks"] for item in data if item["id"] == playlist_id]
        if not trkl:
            return []
        try:
            return [self.lib_tracks[track["trackId"]] for track in trkl[0]]
        except:
            return []

    def create_station_for_genre(self, genre_id):
        id = self.api.create_station("station" + genre_id, genre_id=genre_id)
        return id

    def get_user_stations(self):
        data = self.api.get_all_stations()
        # parse_playlist works fine for stations
        stations = [_parse_playlist(d) for d in data]
        return stations

    def delete_user_station(self, id):
        self.api.delete_stations(id)

    # not working right now
    def listen_now(self):
        print("api.get_listen_now_items()", file=sys.stderr)
        ret = {"albums": [], "stations": []}
        try:
            data = self.api.get_listen_now_items()
        except Exception as err:
            print("api.get_listen_now_items failed: %s" % err, file=sys.stderr)
            data = None

        # listen_now entries are not like normal albums or stations,
        # and need special parsing. I could not make obvious sense of
        # the station-like listen_now entries, so left them aside for
        # now. Maybe should use create_station on the artist id?
        if data:
            ret["albums"] = [_parse_ln_album(a["album"]) for a in data if "album" in a]
            # ret['stations'] = [_parse_ln_station(d['radio_station']) \
            #                   for d in data if 'radio_station' in d]
        else:
            print("listen_now: no items returned !", file=sys.stderr)
        print(
            "get_listen_now_items: returning %d albums and %d stations" % (len(ret["albums"]), len(ret["stations"])),
            file=sys.stderr,
        )
        return ret

    def get_situation_content(self, id=None):
        ret = {"situations": [], "stations": []}
        now = time.time()
        if id is None and now - self.sitdataupdtime > 300:
            self.sitbyid = {}
            self.sitdata = self.api.get_listen_now_situations()
            self.sitdataupdtime = now

        # Root is special, it's a list of situations
        if id is None:
            ret["situations"] = [self._parse_situation(s) for s in self.sitdata]
            return ret

        # not root
        if id not in self.sitbyid:
            print("get_situation_content: %s unknown" % id, file=sys.stderr)
            return ret

        situation = self.sitbyid[id]
        # self.dmpdata("situation", situation)
        if "situations" in situation:
            ret["situations"] = [self._parse_situation(s) for s in situation["situations"]]
        if "stations" in situation:
            ret["stations"] = [_parse_situation_station(s) for s in situation["stations"]]

        return ret

    def _parse_situation(self, data):
        self.sitbyid[data["id"]] = data
        return Playlist(id=data["id"], name=data["title"])

    def create_curated_and_get_tracks(self, id):
        sid = self.api.create_station("station" + id, curated_station_id=id)
        print("create_curated: sid %s" % sid, file=sys.stderr)
        tracks = [_parse_track(t) for t in self.api.get_station_tracks(sid)]
        # print("curated tracks: %s"%tracks, file=sys.stderr)
        self.api.delete_stations(sid)
        return tracks

    def get_station_tracks(self, id):
        return [_parse_track(t) for t in self.api.get_station_tracks(id)]

    def get_media_url(self, song_id, quality=u"med"):
        url = self.api.get_stream_url(song_id, quality=quality)
        print("get_media_url got: %s" % url, file=sys.stderr)
        return url

    def get_album_tracks(self, album_id):
        data = self.api.get_album_info(album_id, include_tracks=True)
        album = _parse_album(data)
        return [_parse_track(t, album) for t in data["tracks"]]

    def get_promoted_tracks(self):
        data = self.api.get_promoted_songs()
        # self.dmpdata("promoted_tracks", data)
        return [_parse_track(t) for t in data]

    def get_genres(self, parent=None):
        data = self.api.get_genres(parent_genre_id=parent)
        return [_parse_genre(g) for g in data]

    def get_artist_info(self, artist_id, doRelated=False):
        ret = {"albums": [], "toptracks": [], "related": []}
        # Happens,some library tracks have no artistId entry
        if artist_id is None or artist_id == "None":
            print("get_artist_albums: artist_id is None", file=sys.stderr)
            return ret
        else:
            print("get_artist_albums: artist_id %s" % artist_id, file=sys.stderr)

        maxrel = 20 if doRelated else 0
        maxtop = 0 if doRelated else 10
        incalbs = False if doRelated else True
        data = self.api.get_artist_info(artist_id, include_albums=incalbs, max_top_tracks=maxtop, max_rel_artist=maxrel)
        # self.dmpdata("artist_info", data)
        if "albums" in data:
            ret["albums"] = [_parse_album(alb) for alb in data["albums"]]
        if "topTracks" in data:
            ret["toptracks"] = [_parse_track(t) for t in data["topTracks"]]
        if "related_artists" in data:
            ret["related"] = [_parse_artist(a) for a in data["related_artists"]]
        return ret

    def get_artist_related(self, artist_id):
        data = self.get_artist_info(artist_id, doRelated=True)
        return data["related"]

    def search(self, query):
        data = self.api.search(query, max_results=50)
        # self.dmpdata("Search", data)

        tr = [_parse_track(i["track"]) for i in data["song_hits"]]
        print("track ok", file=sys.stderr)
        ar = [_parse_artist(i["artist"]) for i in data["artist_hits"]]
        print("artist ok", file=sys.stderr)
        al = [_parse_album(i["album"]) for i in data["album_hits"]]
        print("album ok", file=sys.stderr)
        # self.dmpdata("Search playlists", data['playlist_hits'])
        try:
            pl = [_parse_splaylist(i) for i in data["playlist_hits"]]
        except:
            pl = []
        print("playlist ok", file=sys.stderr)
        return SearchResult(artists=ar, albums=al, playlists=pl, tracks=tr)
Example #27
0
class GMusic(object):
    def __init__(self):
        self.authenticated = False
        self.all_access = False
        self._device = None
        self._webclient = Webclient(debug_logging=False)
        self._mobileclient = Mobileclient(debug_logging=False)
        self._playlists = []
        self._playlist_contents = []
        self._all_songs = []
        self._all_artists = {}
        self._all_albums = {}
        self._all_genres = {}
        self._stations = []

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

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

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

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

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

    def get_all_songs(self, id=None):
        if len(self._all_songs) == 0:
            try:
                self._all_songs = self._mobileclient.get_all_songs()
            except NotLoggedIn:
                if self.authenticate():
                    self._all_songs = self._mobileclient.get_all_songs()
                else:
                    return []

        if id:
            return [x for x in self._all_songs if x['id'] == id][0]
        else:
            return self._all_songs

    def get_all_artists(self):
        if not self._all_artists:
            songs = self.get_all_songs()
            for song in songs:
                artist = song['artist']
                thumb = None
                if artist not in self._all_artists:
                    self._all_artists[artist] = []

                track = {'title': song['title'],
                        'album': song['album'],
                        'artist': artist,
                        'durationMillis': song['durationMillis'],
                        'trackType': song['trackNumber'],
                        'id': song['id']}

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

                if 'artistArtRef' in song:
                    thumb = song['artistArtRef'][0]['url']

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

                self._all_artists[artist].append({'track': track, 'thumb': thumb, 'id': song['id']})

        return self._all_artists

    def get_all_albums(self):
        if not self._all_albums:
            songs = self.get_all_songs()
            for song in songs:
                album = song['album']
                thumb = None
                if album not in self._all_albums:
                    self._all_albums[album] = []

                track = {'title': song['title'],
                        'album': album,
                        'artist': song['artist'],
                        'durationMillis': song['durationMillis'],
                        'trackType': song['trackNumber'],
                        'id': song['id']}

                if 'albumArtRef' in song:
                    track['albumArtRef'] = song['albumArtRef']
                    thumb = song['albumArtRef'][0]['url']

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

                self._all_albums[album].append({'track': track, 'thumb': thumb, 'id': song['id']})

        return self._all_albums

    def get_all_genres(self):
        if not self._all_genres:
            songs = self.get_all_songs()
            for song in songs:
                genre = song['genre']
                if genre not in self._all_genres:
                    self._all_genres[genre] = []

                track = {'title': song['title'],
                        'album': song['album'],
                        'artist': song['artist'],
                        'durationMillis': song['durationMillis'],
                        'trackType': song['trackNumber'],
                        'id': song['id']}

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

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

                self._all_genres[genre].append({'track': track, 'id': song['id']})

        return self._all_genres

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

        return self._playlists

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

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

        return tracks

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

        return playlist

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

        return self._stations

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

        return tracks

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

        return genres

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

        return station

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

        return results

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

        return results

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

        return results

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

        return stream_url
Example #28
0
class Session(object):
    def __init__(self):
        self.api = None
        self.user = None
        self.lib_albums = {}
        self.lib_artists = {}
        self.lib_tracks = {}
        self.lib_playlists = {}
        self.lib_updatetime = 0
        self.sitdata = []
        self.sitbyid = {}
        self.sitdataupdtime = 0
        
    def dmpdata(self, who, data):
        uplog("%s: %s" % (who, json.dumps(data, indent=4)))

    # Look for an Android device id in the registered devices.
    def find_device_id(self, data):
        for entry in data:
            if "type" in entry and entry["type"] == u"ANDROID":
                # Get rid of 0x
                id = entry["id"][2:]
                uplog("Using deviceid %s" % id)
                return id
        return None
    
    def login(self, username, password, deviceid=None):
        self.api = Mobileclient(debug_logging=False)

        if deviceid is None:
            logged_in = self.api.login(username, password,
                                       Mobileclient.FROM_MAC_ADDRESS)
            if logged_in:
                # Try to re-login with a valid deviceid
                data = self.api.get_registered_devices()
                #self.dmpdata("registered devices", data)
                deviceid = self.find_device_id(data)
                if deviceid:
                    logged_in = self.login(username, password, deviceid)
        else:
            logged_in = self.api.login(username, password, deviceid)

        isauth = self.api.is_authenticated()
        #uplog("login: Logged in: %s. Auth ok: %s" % (logged_in, isauth))
        return logged_in

    def _get_user_library(self):
        now = time.time()
        if now - self.lib_updatetime < 300:
            return
        if self.lib_updatetime == 0:
            data = self.api.get_all_songs()
            #self.dmpdata("all_songs", data)
        else:
            data = self.api.get_all_songs(updated_after=datetime.datetime.fromtimestamp(self.lib_updatetime))
            #self.dmpdata("all_songs_since_update", data)
        self.lib_updatetime = now
        tracks = [_parse_track(t) for t in data]
        self.lib_tracks.update(dict([(t.id, t) for t in tracks]))
        for track in tracks:
            # We would like to use the album id here, but gmusic
            # associates the tracks with any compilations after
            # uploading (does not use the metadata apparently), so
            # that we can't (we would end up with multiple
            # albums). OTOH, the album name is correct (so seems to
            # come from the metadata). What we should do is test the
            # album ids for one album with a matching title, but we're
            # not sure to succeed. So at this point, the album id we
            # end up storing could be for a different albums, and we
            # should have a special library-local get_album_tracks
            self.lib_albums[track.album.name] = track.album
            self.lib_artists[track.artist.id] = track.artist
            
    def get_user_albums(self):
        self._get_user_library()
        return self.lib_albums.values()

    def get_user_artists(self):
        self._get_user_library()
        return self.lib_artists.values()

    def get_user_playlists(self):
        pldata = self.api.get_all_playlists()
        #self.dmpdata("playlists", pldata)
        return [_parse_playlist(pl) for pl in pldata]

    def get_user_playlist_tracks(self, playlist_id):
        self._get_user_library()
        # Unfortunately gmusic does not offer incremental updates for
        # playlists.  This means we must download all playlist data any
        # time we want an update.  Playlists include track information
        # for gmusic songs, so if a user has a lot of playlists this
        # can take some time.
        # For now, we only load the playlists one time for performance purposes
        if len(self.lib_playlists) == 0:
            data = self.api.get_all_user_playlist_contents()
            self.lib_playlists = dict([(pl['id'], pl) for pl in data])
        tracks = []
        if playlist_id in self.lib_playlists:
            for entry in self.lib_playlists[playlist_id]['tracks']:
                if entry['deleted']:
                    continue
                if entry['source'] == u'1':
                    tracks.append(self.lib_tracks[entry['trackId']])
                elif 'track' in entry:
                    tracks.append(_parse_track(entry['track']) )
        return tracks

    def create_station_for_genre(self, genre_id):
        id = self.api.create_station("station"+genre_id, genre_id=genre_id)
        return id

    def get_user_stations(self):
        data = self.api.get_all_stations()
        # parse_playlist works fine for stations
        stations = [_parse_playlist(d) for d in data]
        return stations

    def delete_user_station(self, id):
        self.api.delete_stations(id)

    # not working right now
    def listen_now(self):
        print("api.get_listen_now_items()", file=sys.stderr)
        ret = {'albums' : [], 'stations' : []}
        try:
            data = self.api.get_listen_now_items()
        except Exception as err:
            print("api.get_listen_now_items failed: %s" % err, file=sys.stderr)
            data = None

        # listen_now entries are not like normal albums or stations,
        # and need special parsing. I could not make obvious sense of
        # the station-like listen_now entries, so left them aside for
        # now. Maybe should use create_station on the artist id?
        if data:
            ret['albums'] = [_parse_ln_album(a['album']) \
                             for a in data if 'album' in a]
            #ret['stations'] = [_parse_ln_station(d['radio_station']) \
            #                   for d in data if 'radio_station' in d]
        else:
            print("listen_now: no items returned !", file=sys.stderr)
        print("get_listen_now_items: returning %d albums and %d stations" %\
              (len(ret['albums']), len(ret['stations'])), file=sys.stderr)
        return ret

    def get_situation_content(self, id = None):
        ret = {'situations' : [], 'stations' : []}
        now = time.time()
        if id is None and now - self.sitdataupdtime > 300:
            self.sitbyid = {}
            self.sitdata = self.api.get_listen_now_situations()
            self.sitdataupdtime = now

        # Root is special, it's a list of situations
        if id is None:
            ret['situations'] = [self._parse_situation(s) \
                                 for s in self.sitdata]
            return ret
        
        # not root
        if id not in self.sitbyid:
            print("get_situation_content: %s unknown" % id, file=sys.stderr)
            return ret

        situation = self.sitbyid[id]
        #self.dmpdata("situation", situation)
        if 'situations' in situation:
            ret['situations'] = [self._parse_situation(s) \
                                 for s in situation['situations']]
        if 'stations' in situation:
            ret['stations'] = [_parse_situation_station(s) \
                               for s in situation['stations']]

        return ret

    def _parse_situation(self, data):
        self.sitbyid[data['id']] = data
        return Playlist(id=data['id'], name=data['title'])
        
    def create_curated_and_get_tracks(self, id):
        sid = self.api.create_station("station"+id, curated_station_id=id)
        print("create_curated: sid %s"%sid, file=sys.stderr)
        tracks = [_parse_track(t) for t in self.api.get_station_tracks(sid)]
        #print("curated tracks: %s"%tracks, file=sys.stderr)
        self.api.delete_stations(sid)
        return tracks
    
    def get_station_tracks(self, id):
        return [_parse_track(t) for t in self.api.get_station_tracks(id)]
    
    def get_media_url(self, song_id, quality=u'med'):
        url = self.api.get_stream_url(song_id, quality=quality)
        print("get_media_url got: %s" % url, file=sys.stderr)
        return url

    def get_album_tracks(self, album_id):
        data = self.api.get_album_info(album_id, include_tracks=True)
        album = _parse_album(data)
        return [_parse_track(t, album) for t in data['tracks']]

    def get_promoted_tracks(self):
        data = self.api.get_promoted_songs()
        #self.dmpdata("promoted_tracks", data)
        return [_parse_track(t) for t in data]

    def get_genres(self, parent=None):
        data = self.api.get_genres(parent_genre_id=parent)
        return [_parse_genre(g) for g in data]
                
    def get_artist_info(self, artist_id, doRelated=False):
        ret = {"albums" : [], "toptracks" : [], "related" : []} 
        # Happens,some library tracks have no artistId entry
        if artist_id is None or artist_id == 'None':
            uplog("get_artist_albums: artist_id is None")
            return ret
        else:
            uplog("get_artist_albums: artist_id %s" % artist_id)

        maxrel = 20 if doRelated else 0
        maxtop = 0 if doRelated else 10
        incalbs = False if doRelated else True
        data = self.api.get_artist_info(artist_id, include_albums=incalbs,
                                        max_top_tracks=maxtop,
                                        max_rel_artist=maxrel)
        #self.dmpdata("artist_info", data)
        if 'albums' in data:
            ret["albums"] = [_parse_album(alb) for alb in data['albums']]
        if 'topTracks' in data:
            ret["toptracks"] = [_parse_track(t) for t in data['topTracks']]
        if 'related_artists' in data:
            ret["related"] = [_parse_artist(a) for a in data['related_artists']]
        return ret

    def get_artist_related(self, artist_id):
        data = self.get_artist_info(artist_id, doRelated=True)
        return data["related"]
    
    def search(self, query):
        data = self.api.search(query, max_results=50)
        #self.dmpdata("Search", data)

        tr = [_parse_track(i['track']) for i in data['song_hits']]
        ar = [_parse_artist(i['artist']) for i in data['artist_hits']]
        al = [_parse_album(i['album']) for i in data['album_hits']]
        #self.dmpdata("Search playlists", data['playlist_hits'])
        try:
            pl = [_parse_splaylist(i) for i in data['playlist_hits']]
        except:
            pl = []
        return SearchResult(artists=ar, albums=al, playlists=pl, tracks=tr)
Example #29
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 #30
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('-d', '--dst', help='Perform operation on the dst account', action='store_true', dest='dst')
    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()
    if args.dst:
        api.login(dst_user, dst_pass, dst_device)    
    else:
        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()

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

    all_playlist_entries = api.get_all_user_playlist_contents()

    selected_playlist_entries = []
    
    for entry in all_playlist_entries:
        if entry['id'] == args.playlist:
            selected_playlist_entries = entry['tracks']

    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 = []

    playlist_tracks_reversed = []

    for track in playlist_tracks:
        playlist_tracks_reversed.insert(0, track)

    for track in playlist_tracks_reversed:
        track['rating'] = '5'
        res = api.change_song_metadata(track)

        if len(res) != 1:
            raise Exception('Could not change track metadata!')

        time.sleep(1)
Example #31
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()
Example #32
0
        pickle.dump(self,file)
        file.close()


def dictToSong(dict):
    return Song(dict)


if(len(sys.argv) < 1):
    print "please enter the name of the playlist"
    exit()

googlePlay = Mobileclient()
logged_in = googlePlay.login('*****@*****.**', '1944_D-d@y',Mobileclient.FROM_MAC_ADDRESS)
if(not logged_in):
    print "failed to login to google play"
    exit()
print "Choose a playlist: "
playlists = googlePlay.get_all_playlists()
for playlist in playlists:
    print playlist["name"]

list = Playlist()
list.fetchFromGooglePlay(googlePlay)
list.pushToSpotify()
#for song in list.songs:
    #print song.name
#list.fetchFromGooglePlay(googlePlay)
list.outToFile()
#list.pushToSpotify()
Example #33
0
class GMusicDownloader(threading.Thread):
    api = None
    library = list()
    filtered_library = list()
    download_workers = list()
    max_threads = None
    config_error = False
    loggedin = False
    playlists = None
    fetchedlists = None  # type: list
    filecreationlock = threading.Lock()
    trackqueue = Queue()
    threadqueue = Queue()
    player = None
    current_displayed_content_type = "Track"

    def __init__(self, queue: Queue):
        super().__init__()
        self.communicationqueue = queue
        self.api = Mobileclient()
        config = configparser.ConfigParser()
        config.read("config.ini")
        self.load_settings(config)
        if not self.config_error:
            self.threaded_api_query(self.login)
        for i in range(self.max_threads):
            self.threadqueue.put("I'm a thread")

    def login(self):
        if not self.loggedin:
            self.api.login(self.username, self.password,
                           Mobileclient.FROM_MAC_ADDRESS)
            print("logged in")
            self.library = self.api.get_all_songs()
            print("songs fetched")
            self.communicationqueue.put({
                "login": self.username,
                "library": self.library
            })
            self.loggedin = True

    def get_directory_path(self, track: dict, and_create=False):
        artist = self.slugify(track["artist"])
        album = self.slugify(track["album"])
        artist_path = os.path.join(self.music_directory, artist)
        album_path = os.path.join(artist_path, album)
        if and_create:
            if not os.path.exists(artist_path):
                os.makedirs(artist_path)
            if not os.path.exists(album_path):
                os.makedirs(album_path)
        return album_path

    def get_file_path(self, track: dict, directory_path: str = None):
        if directory_path is not None:
            return os.path.join(directory_path,
                                self.slugify(track["title"]) + self.file_type)
        else:
            return os.path.join(self.get_directory_path(track),
                                self.slugify(track["title"]) + self.file_type)

    def threaded_stream_downloads(self, tracklist: list):
        for i in range(self.max_threads):
            threading.Thread(target=self.__downloadworker).start()
        for track in tracklist:
            self.trackqueue.put(track)
        # stop threads when they're done
        for i in range(self.max_threads):
            self.trackqueue.put(None)

    def __downloadworker(self):
        while True:
            permission = self.threadqueue.get(block=True)
            track = self.trackqueue.get()
            if track is None:
                self.threadqueue.put(permission)
                break
            self.communicationqueue.put({"downloading": track})
            self.stream_download(track)
            self.threadqueue.put(permission)
            self.trackqueue.task_done()

    def stream_download(self, track: dict):
        track_title = track["title"]

        self.filecreationlock.acquire()
        directory_path = self.get_directory_path(track, and_create=True)
        self.filecreationlock.release()

        file_path = self.get_file_path(track, directory_path)

        if not os.path.exists(file_path):
            dl = 0
            track_url = self.api.get_stream_url(track['id'])
            response = requests.get(track_url, stream=True)
            # total_length = int(response.headers.get('content-length'))
            with open(file_path, "wb") as songfile:
                for chunk in response.iter_content(chunk_size=self.chunk_size):
                    songfile.write(chunk)
                    dl += len(chunk)
            print(track_title, " done.")
            # next(filter(lambda t: t == track, self.filtered_library))
        else:
            print(track_title + " already exists, skipping")
        self.add_tags(file_path, track)
        self.communicationqueue.put({"download complete": track})

    def search_library(self, search_term: str):
        if search_term == "*":
            self.filtered_library = self.library
        else:
            self.filtered_library = list(
                filter(lambda t: search_term in t["artist"], self.library))

    @staticmethod
    def threaded_api_query(worker: types.FunctionType, *args):
        threading.Thread(target=worker, args=(*args, )).start()

    def search_worker_thread(self, searchstring: str):
        search_results = self.api.search(self.slugify(searchstring))

        def parse_song_hit(song_hit):
            # couldn't fit it into a lambda :'(
            track = song_hit["track"]
            track["id"] = track["storeId"]
            return track

        self.filtered_library = list(
            map(parse_song_hit, search_results["song_hits"]))
        self.communicationqueue.put({"search results": True})

    @staticmethod
    def slugify(value):
        """
        Normalizes string, removes non-alpha characters
        """
        value = unicodedata.normalize('NFKD',
                                      value).encode('ascii',
                                                    'ignore').decode('utf-8')
        value = re.sub('[^\w\s-]', '', value).strip()
        return value

    def check_filtered_tracks_for_download(self):
        for track in self.filtered_library:
            if "saved" not in track:
                if os.path.exists(self.get_file_path(track)):
                    track["saved"] = "√"
                else:
                    track["saved"] = ""

    def sort(self, sort: str, is_reversed: bool, content_type: str):
        if content_type == "Track":
            self.filtered_library = sorted(self.filtered_library,
                                           key=lambda k: k[sort],
                                           reverse=is_reversed)
        elif content_type == "Playlist":

            def lazy_hack_for_playlist_sort(playlist: dict):
                if sort == "title":
                    return playlist["name"]
                if sort == "artist":
                    return playlist["ownerName"]
                if sort == "album":
                    return playlist['type']

            self.playlists = sorted(self.playlists,
                                    key=lazy_hack_for_playlist_sort,
                                    reverse=is_reversed)

    def load_settings(self, config: configparser.ConfigParser):
        try:
            account = config["Account"]
            self.username = account["username"]
            self.password = account["password"]
            settings = config["Settings"]
            self.music_directory = settings["music_directory"]
            self.file_type = "." + settings["file_type"]
            self.chunk_size = settings.getint("chunk_size")
            self.max_threads = settings.getint("download_threads", 5)
            self.config_error = False
        except KeyError as e:
            self.communicationqueue.put({
                "error": {
                    "title":
                    "Configuration Error GMusicDownloader",
                    "body":
                    "Could not find " + e.args[0] +
                    " in preferences, please update prefs and try again"
                }
            })
            self.config_error = True

    def add_tags(self, filepath: str, track: dict):
        try:
            tags = EasyID3(filepath)
        except ID3NoHeaderError:
            tags = mutagen.File(filepath, easy=True)
            tags.add_tags()

        tags["tracknumber"] = str(
            track["trackNumber"]).encode("utf-8").decode("utf-8")
        tags["title"] = track["title"]
        tags["artist"] = track["artist"]
        tags["album"] = track["album"]
        tags["discnumber"] = str(
            track["discNumber"]).encode("utf-8").decode("utf-8")
        tags["genre"] = track["genre"]
        tags["composer"] = track["composer"]
        tags["albumartist"] = track["albumArtist"]
        if "beatsPerMinute" in track and not track["beatsPerMinute"] == 0:
            tags["bpm"] = str(
                track["beatsPerMinute"]).encode("utf-8").decode("utf-8")
        # TODO store Year. will have to use standard ID3 instead of easy
        tags.save(v2_version=3)

    def open_playlists(self):
        if self.playlists is None:
            self.playlists = self.api.get_all_playlists()
        if not "lastAdded" in map(lambda p: p["id"], self.playlists):
            self.add_automatic_playlists()
        self.communicationqueue.put({"playlists loaded": self.playlists})

    def add_automatic_playlists(self):
        last_added = {
            "id":
            'lastAdded',
            "tracks":
            sorted(self.library,
                   key=lambda t: t['creationTimestamp'],
                   reverse=True),
            "name":
            "Last Added",
            "ownerName":
            "System",
            "type":
            "Automatic"
        }
        self.playlists.append(last_added)

        thumbs_up = {
            "id": 'thumbsup',
            "tracks": filter(lambda t: t['rating'] > 3, self.library),
            "name": "Thumbs Up",
            "ownerName": "System",
            "type": "Automatic"
        }
        self.playlists.append(thumbs_up)

    def fetch_all_playlists_and_return_one_with_iid(self, iid: str):
        # TODO make this work for non user owned playlists. should use get_shared_playlist_contents for those.
        if self.fetchedlists is None:
            self.fetchedlists = self.api.get_all_user_playlist_contents(
            )  # type: list
            # noinspection PyTypeChecker
            for playlist in self.fetchedlists:
                existing_playlist = next(
                    filter(lambda p: p["id"] == playlist["id"],
                           self.playlists))
                existing_playlist["tracks"] = playlist["tracks"]

        # noinspection PyTypeChecker
        for playlist in self.playlists:
            if playlist["id"] == iid:
                playlist_tracks = playlist["tracks"]
                if playlist["type"] == "Automatic":
                    self.filtered_library = playlist_tracks
                else:
                    self.filtered_library = self.songs_from_playlist(
                        playlist_tracks)
                self.communicationqueue.put({"search results": True})
                return
        self.current_displayed_content_type = "Playlist"
        self.communicationqueue.put(
            {"error": {
                "title": "Could not fetch playlist",
                "body": ":("
            }})

    def songs_from_playlist(self, playlist):
        tracks = list()
        for id_dict in playlist:
            if "track" in id_dict:
                track = id_dict["track"]
            else:
                track = next(
                    filter(lambda t: t["id"] == id_dict['trackId'],
                           self.library), None)
            if "id" not in track:
                track["id"] = track["storeId"]
            tracks.append(track)
        return tracks

    def play_song(self, trackid):
        try:
            import vlc
            if isinstance(trackid, dict):
                print('playing track', trackid['title'])
                trackid = trackid["id"]
            if trackid is None and self.player is not None:
                self.player.pause()
                return
            url = self.api.get_stream_url(trackid)
            if self.player is None:
                self.player = vlc.MediaPlayer(url)  # type: vlc.MediaPlayer
            else:
                # TODO: this is terrible and I should fix it. Lucky it works!
                self.player = vlc.MediaPlayer(url)
            self.player.play()
            self.player.event_manager().event_attach(
                vlc.EventType.MediaPlayerEndReached, self.song_complete,
                trackid)
        except ImportError:
            self.communicationqueue.put({
                "error": {
                    "title": "Could not import VLC",
                    "body": "Please make sure you have 64-bit VLC installed"
                }
            })

    @callbackmethod
    def song_complete(self, event, trackId):
        if self.current_displayed_content_type == "Track":
            libiterator = iter(self.filtered_library)
            for track in libiterator:
                if track["id"] == trackId:
                    self.play_song(next(libiterator, None))
                    return
Example #34
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
			#Add to the path dictionary
			playlistFilePaths.append((os.path.join(currDir, iFile)).replace('\\','/'))

#Get the file name
#path = tkFileDialog.askopenfilename()

#Create the mobile client
#No debug logging, validate, and verify SSL
api = Mobileclient(False,True,True)

#Login
#api.perform_oauth('C:\\Users\\Joshv\\Josh\\Programming\\Python\\googleMusic\\gMusicDeviceID.txt')
api.oauth_login('b83fce595c6fd82bcfcfdd8e2e542d79f3c176bb0974c4bb34da2df10d95cec1')

#Retrieve all playlists
playlists = api.get_all_playlists(False, False)

#Retrive the contents of all playlists
playlistContents = api.get_all_user_playlist_contents()

#Get the entire song library
library = api.get_all_songs(False, False)

#Loop through all of the local playlists
for playlistFilePath in playlistFilePaths:
	#Create the array for the paths artists, tracks in the local playlist
	paths = []
	lArtists = []
	lTracks = []

	#Create arrays for the artists and tracks in the gmusic playlist
Example #36
0
    JOIN `gpm_albums` USING (`gpm_albumid`)
    WHERE `gpm_playlists`.`name` LIKE 'Background %'
        AND `gpm_playlists`.`deleted` = 0
        AND `gpm_playlist_entries`.`deleted` = 0
    ORDER BY RAND()
    LIMIT 950) AS `t`
    ORDER BY `album_artist` ASC, `year` ASC, `album` ASC, `title` ASC
"""
cursor.execute(sql)
trackRows = cursor.fetchall()

playlistName = "BG Rand " + datetime.now().strftime("%Y-%m %B")

# Check if the playlist exists
# If it does, abort unless --replace is specified, in which case delete the old list
playlists = gpm.get_all_playlists()
for playlist in playlists:
    if playlist["deleted"]:
        continue
    if playlist["name"] == playlistName:
        if args.replace:
            logger.info("Deleting existing playlist")
            gpm.delete_playlist(playlist["id"])
        else:
            logger.error("Playlist already exists: " + playlistName)
            exit(1)

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

logger.info("Adding " + str(len(trackRows)) + " tracks to playlist")
Example #37
0
from datetime import date
import fileinput

if len(sys.argv) > 1:

    pw = ""
    for line in fileinput.input():
        pw = line

    api = Mobileclient()
    logged_in = api.login('*****@*****.**', pw,
                          Mobileclient.FROM_MAC_ADDRESS)

    if logged_in:

        playlists = api.get_all_playlists()
        names = []

        for playlist in playlists:
            names.append(playlist['name'])

        for name in sorted(names):
            print(name)

    api.logout()

else:
    print(
        "Please include the path to a file containing a Google Music password."
    )
Example #38
0

def transfer_gpm_to_ytm():
    authenticated = do_login()
    print(f'Authentication status: {authenticated}')
    if not authenticated:
        return
    client.get_all_songs()
    client.get_all_playlists()


if __name__ == '__main__':
    print('Logging in...')
    if do_login():
        tracks = client.get_all_songs()
        ids = [
            '648b82b7-015f-3823-bf21-3d31d7dc822d',
            '02a9b776-8606-37bd-8a92-c723e558c8d5'
        ]
        pEntry = None
        for track in tracks:
            if track['id'] in ids or track.get(
                    'storeId', None) in ids or track.get('nid', None) in ids:
                pEntry = track
        if pEntry is not None:
            print('Playlist Entry')
            print(dumps(pEntry, indent=4))
        print(dumps(tracks[0], indent=4))
        print(dumps(client.get_all_playlists()[0], indent=4))
        print(dumps(client.get_all_user_playlist_contents()[0], indent=4))
Example #39
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 #40
0
def auth():
    global wc, mc
    if os.path.isfile("email.txt"):
        f = open("email.txt", "r")
        email = f.read()
    else:
        f = open("email.txt", "w")

        email = emailBox()
        email.connect("delete-event", Gtk.main_quit)
        email.show_all()
        Gtk.main()
        f.write(email.email)
        f.close()
        email = email.email
    # Runs the dialog to take the user password
    pwdb = passwordBox()
    pwdb.connect("delete-event", Gtk.main_quit)
    pwdb.show_all()
    Gtk.main()

    wc.login(email, pwdb.pwd)
    mc.login(email, pwdb.pwd, get_mac())

mc = Mobileclient(debug_logging=True, validate=True, verify_ssl=True)
wc = Webclient(debug_logging=True, validate=True, verify_ssl=True)
auth()
playlist_list = mc.get_all_playlists(incremental=False, include_deleted=False)
print([x.get("name") for x in playlist_list])
class MusicPlayer:
    def __init__(self, device_id, token):
        # Gmusic API initialization
        print("initializing client")
        self.api = Mobileclient()
        print("logging in")
        self.api.oauth_login(device_id, token)
        print("loading all songs")
        self.all_songs = self.api.get_all_songs()
        print("loading playlists")
        self.all_playlists = self.api.get_all_user_playlist_contents()
        self.all_playlist_names = {}
        for playlist in self.all_playlists:
            self.all_playlist_names[playlist["name"]] = playlist["id"]

        # VLC initialization
        self.track_file = vlc.MediaPlayer()
        self.track_list = []
        self.titles = []
        self.track_number = 0
        self.playlists = []
        self.current_time = -1
        self.max_time = -1

        # Get playlists, songs from the first playlist, and load the first song
        self.get_playlists()
        self.get_songs_from_playlist(self.playlists[0])
        self.song = self.track_list[self.track_number]["trackId"]
        self.load_track()

        # GUI initialization
        print("creating window")
        self.song = ""
        self.player_layout = [
            [sg.Text("I love you Mom!", size=(15, 1), font=("Helvetica", 25))],
            [sg.Listbox(values=self.playlists, size=(30, 20), bind_return_key=True, key="_playlists_"),
             # sg.Image(),
             sg.Listbox(values=self.titles, size=(30, 20), bind_return_key=True, key="_Tracks_")],
            [sg.Text("Click Play or select song", key="_SongName_", enable_events=True)],
            [sg.Text("Volume:"), sg.Slider(range=(0, 100), orientation="h", size=(20, 15),
                                           default_value=self.track_file.audio_get_volume(), key="_volume_"),
             sg.Button("Play"), sg.Button("Pause"), sg.Button("Next")]
        ]

        self.title = "Music Player"
        self.window = sg.Window(self.title).Layout(self.player_layout)

    def get_playlists(self):
        data = self.api.get_all_playlists()
        self.playlists = []
        for playlist in data:
            if not playlist['deleted']:
                self.playlists.append(playlist['name'])
        print(self.playlists)

    def change_playlists(self, name):
        for pos, title in enumerate(self.playlists):
            if title == name:
                self.get_songs_from_playlist(self.playlists[pos])

    def get_songs_from_playlist(self, name):
        print("Obtaining track list")
        tracks = []
        if name in self.all_playlist_names:
            for playlist in self.all_playlists:
                if playlist["name"] == name:
                    for track in playlist["tracks"]:
                        tracks.append(track)
                    break
        self.track_list = tracks
        self.get_playlist_song_titles()

    def get_playlist_song_titles(self):
        print("Getting playlist song titles")
        titles = []
        for song in self.track_list:
            if song["source"] == "2":
                titles.append(song["track"]["title"])
            else:
                for track in self.all_songs:
                    if track["id"] == song["trackId"]:
                        print("match found")
                        titles.append(track["title"])
                else:
                    print("No match found")
        print(titles)
        self.titles = titles

    def get_song_position_from_title(self, title):
        for pos, name in enumerate(self.titles):
            if name == title:
                return pos
        else:
            print("Couldn't find song in tracks")

    def download_song(self):
        print("downloading song")
        url = self.api.get_stream_url(self.song)
        doc = requests.get(url)
        with open("song.mp3", "wb") as f:
            f.write(doc.content)

    def load_track(self):
        self.track_file = vlc.MediaPlayer("song.mp3")
        print("Time:", self.track_file.get_length())

    def play(self):
        self.track_file.play()
        self.window.FindElement("_SongName_").Update(value=self.titles[self.track_number])

    def stop(self):
        self.track_file.stop()

    def pause(self):
        self.track_file.pause()

    def next(self):
        self.track_number += 1
        self.song = self.track_list[self.track_number]["trackId"]
        self.window.FindElement("_Tracks_").SetValue(self.titles[self.track_number])
        self.download_song()
        self.track_file.stop()
        self.load_track()
        self.track_file.play()
        self.max_time = self.track_file.get_time()

    def run(self):
        print("launching program")
        while True:
            self.current_time = self.track_file.get_time()
            if self.max_time == -1:
                self.max_time = self.track_file.get_length()
            elif self.max_time == 0:
                self.max_time = -1
            else:
                current = timedelta(milliseconds=self.current_time)
                max = timedelta(milliseconds=self.max_time)
                # print("Current", current, "Max", max)
                # print(int((self.current_time / self.max_time) * 100))

                if (self.current_time + 500) > self.max_time:
                    self.next()

            event, values = self.window.Read(timeout=100)
            if event is not None:
                if event == "Play":
                    self.play()
                elif event == "Stop":
                    self.stop()
                elif event == "Pause":
                    self.pause()
                elif event == "Next":
                    self.next()
                elif event == "_Tracks_":
                    self.track_number = self.get_song_position_from_title(values[event][0])
                    self.song = self.track_list[self.track_number]["trackId"]
                    self.download_song()
                    self.track_file.stop()
                    self.load_track()
                    self.play()
                elif event == "_playlists_":
                    print(values[event][0])
                    self.change_playlists(values[event][0])
                    self.window.FindElement("_Tracks_").Update(self.titles)
                elif event == "_volume_":
                    print("Volume", event, values)
                else:
                    self.track_file.audio_set_volume(values["_volume_"])
            if event == "Quit" or values is None:
                break
    print(
        "Could not connect to Play Music, authentication failure. Exiting...")
    exit(1)

#Ensure the device/account actually has a google play subscription and is able to play/download/etc content
if mc.is_subscribed:
    print("This account DOES have a Play Music subscription!")
else:
    print("This account deos NOT have a Play Music subscription! Exiting...")
    exit(1)

#Get a list of the songs already in the account's library so we can compare songs we're looking for to it and ensure we don't add multiples of songs
owned_songs = mc.get_all_songs()

#Get a list of all playlists the user has so we can be sure not to try a name for ours that's already taken
users_playlists = mc.get_all_playlists(incremental=False)

#Create a name for the playlist, utilizing a root name so that we can add numbers to the end appropriately in the event that a playlist with our chosen name already exists
playlist_root_name = "Nostalgia Playlist " + str(billboard_date.year)
playlist_name = playlist_root_name

#We're going to want to check to make sure that this name isn't already used for a playlist
users_playlists_names = []
for user_playlist in users_playlists:
    users_playlists_names.append(user_playlist["name"])

#Start this number at 2 since that's most likely what a human would do (playlist, playlist2, playlist3, etc)
appendable_num = 2
while playlist_name in users_playlists_names:
    playlist_name = playlist_root_name + " " + str(appendable_num)
    appendable_num = appendable_num + 1
Example #43
0
class GMusicSession(object):
    def __init__(self):
        super(GMusicSession, self).__init__()
        logger.info('Mopidy uses Google Music')
        self.api = Mobileclient()

    def login(self, username, password, deviceid):
        if self.api.is_authenticated():
            self.api.logout()
        try:
            self.api.login(username, password)
        except CallFailure as error:
            logger.error(u'Failed to login as "%s": %s', username, error)
        if self.api.is_authenticated():
            if deviceid is None:
                self.deviceid = self.get_deviceid(username, password)
            else:
                self.deviceid = deviceid
        else:
            return False

    def logout(self):
        if self.api.is_authenticated():
            return self.api.logout()
        else:
            return True

    def get_all_songs(self):
        if self.api.is_authenticated():
            return self.api.get_all_songs()
        else:
            return {}

    def get_stream_url(self, song_id):
        if self.api.is_authenticated():
            try:
                return self.api.get_stream_url(song_id, self.deviceid)
            except CallFailure as error:
                logger.error(u'Failed to lookup "%s": %s', song_id, error)

    def get_all_user_playlist_contents(self):
        if self.api.is_authenticated():
            return self.api.get_all_user_playlist_contents()
        else:
            return {}

    def get_shared_playlist_contents(self, shareToken):
        if self.api.is_authenticated():
            return self.api.get_shared_playlist_contents(shareToken)
        else:
            return {}

    def get_all_playlists(self):
        if self.api.is_authenticated():
            return self.api.get_all_playlists()
        else:
            return {}

    def get_deviceid(self, username, password):
        logger.warning(u'No mobile device ID configured. '
                       u'Trying to detect one.')
        webapi = Webclient(validate=False)
        webapi.login(username, password)
        devices = webapi.get_registered_devices()
        deviceid = None
        for device in devices:
            if device['type'] == 'PHONE' and device['id'][0:2] == u'0x':
                # Omit the '0x' prefix
                deviceid = device['id'][2:]
                break
        webapi.logout()
        if deviceid is None:
            logger.error(u'No valid mobile device ID found. '
                         u'Playing songs will not work.')
        else:
            logger.info(u'Using mobile device ID %s', deviceid)
        return deviceid

    def get_track_info(self, store_track_id):
        if self.api.is_authenticated():
            try:
                return self.api.get_track_info(store_track_id)
            except CallFailure as error:
                logger.error(u'Failed to get All Access track info: %s', error)

    def get_album_info(self, albumid, include_tracks=True):
        if self.api.is_authenticated():
            try:
                return self.api.get_album_info(albumid, include_tracks)
            except CallFailure as error:
                logger.error(u'Failed to get All Access album info: %s', error)
Example #44
0
class TheGoogs:

    DEFAULT_CREDS_DIR = "~/gmusic/.oauth"
    DEFAULT_MOBILE_DEVICE_ID = "342e914abacc484d"  # Galaxy Tab
    DEFAULT_MANAGER_MAC_ADDRESS = "A2:C2:E2:CC:C7:37"  # Made-up

    def __init__(self, creds_dir=DEFAULT_CREDS_DIR):
        self.creds_dir = os.path.expanduser(creds_dir)
        logger.info("Creating TheGoogs from creds at {}".format(
            self.creds_dir))
        self.mobile_creds = os.path.join(self.creds_dir, "mobile.creds")
        self.manager_creds = os.path.join(self.creds_dir, "manager.creds")

        self.mobile = Mobileclient()
        self.manager = Musicmanager()

        logger.debug("Logging in")
        self.mobile.oauth_login(device_id=self.DEFAULT_MOBILE_DEVICE_ID,
                                oauth_credentials=self.mobile_creds)
        self.manager.login(uploader_id=self.DEFAULT_MANAGER_MAC_ADDRESS,
                           oauth_credentials=self.manager_creds)

    def get_libdata(self):
        logger.info("Fetching libdata ...")

        logger.info("... fetching registered devices")
        registered_devices = self.mobile.get_registered_devices()

        logger.info("... fetching all songs")
        library = self.mobile.get_all_songs()

        logger.info("... fetching playlist metadata")
        playlists = self.mobile.get_all_playlists()

        logger.info("... fetching playlist contents")
        playlist_contents = self.mobile.get_all_user_playlist_contents()

        logger.info("... fetching uploaded songs")
        uploaded_songs = self.manager.get_uploaded_songs()

        logger.info("... fetching purchased songs")
        purchased_songs = self.manager.get_purchased_songs()

        return Libdata(timestamp=datetime.utcnow(),
                       registered_devices=registered_devices,
                       all_songs=library,
                       playlist_metadata=playlists,
                       playlists=playlist_contents,
                       uploaded_songs=uploaded_songs,
                       purchased_songs=purchased_songs)

    def get_streamed_song(self, id):
        logger.info("Downloading streamed song id {}".format(id))
        stream_url = self.mobile.get_stream_url(id)
        response = urllib.request.urlopen(stream_url)
        return response.read()

    def get_uploaded_song(self, id):
        logger.info("Downloading uploaded song id {}".format(id))
        suggested_filename, data = self.manager.download_song(id)
        return data
Example #45
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'] + ')'
Example #46
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 #47
0
class GMusicSession(object):

    def __init__(self):
        super(GMusicSession, self).__init__()
        logger.info('Mopidy uses Google Music')
        self.api = Mobileclient()

    def login(self, username, password, deviceid):
        if self.api.is_authenticated():
            self.api.logout()
        try:
            self.api.login(username, password)
        except CallFailure as error:
            logger.error(u'Failed to login as "%s": %s', username, error)
        if self.api.is_authenticated():
            if deviceid is None:
                self.deviceid = self.get_deviceid(username, password)
            else:
                self.deviceid = deviceid
        else:
            return False

    def logout(self):
        if self.api.is_authenticated():
            return self.api.logout()
        else:
            return True

    def get_all_songs(self):
        if self.api.is_authenticated():
            return self.api.get_all_songs()
        else:
            return {}

    def get_stream_url(self, song_id):
        if self.api.is_authenticated():
            try:
                return self.api.get_stream_url(song_id, self.deviceid)
            except CallFailure as error:
                logger.error(u'Failed to lookup "%s": %s', song_id, error)

    def get_all_playlist_contents(self):
        if self.api.is_authenticated():
            return self.api.get_all_user_playlist_contents()
        else:
            return {}

    def get_shared_playlist_contents(self, shareToken):
        if self.api.is_authenticated():
            return self.api.get_shared_playlist_contents(shareToken)
        else:
            return {}

    def get_all_playlists(self):
        if self.api.is_authenticated():
            return self.api.get_all_playlists()
        else:
            return {}

    def get_deviceid(self, username, password):
        logger.warning(u'No mobile device ID configured. '
                       u'Trying to detect one.')
        webapi = Webclient(validate=False)
        webapi.login(username, password)
        devices = webapi.get_registered_devices()
        deviceid = None
        for device in devices:
            if device['type'] == 'PHONE' and device['id'][0:2] == u'0x':
                # Omit the '0x' prefix
                deviceid = device['id'][2:]
                break
        webapi.logout()
        if deviceid is None:
            logger.error(u'No valid mobile device ID found. '
                         u'Playing songs will not work.')
        else:
            logger.info(u'Using mobile device ID %s', deviceid)
        return deviceid

    def get_track_info(self, store_track_id):
        if self.api.is_authenticated():
            try:
                return self.api.get_track_info(store_track_id)
            except CallFailure as error:
                logger.error(u'Failed to get All Access track info: %s', error)

    def get_album_info(self, albumid, include_tracks=True):
        if self.api.is_authenticated():
            try:
                return self.api.get_album_info(albumid, include_tracks)
            except CallFailure as error:
                logger.error(u'Failed to get All Access album info: %s', error)