class GoogleMusicApi(): def __init__(self): self.xbmc = sys.modules["__main__"].xbmc self.xbmcgui = sys.modules["__main__"].xbmcgui self.xbmcplugin = sys.modules["__main__"].xbmcplugin self.settings = sys.modules["__main__"].settings self.language = sys.modules["__main__"].language self.dbg = sys.modules["__main__"].dbg self.common = sys.modules["__main__"].common self.storage = sys.modules["__main__"].storage self.gmusicapi = Api() self.login = GoogleMusicLogin.GoogleMusicLogin(self.gmusicapi) def getPlaylistSongs(self, playlist_id, forceRenew=False): if not self.storage.isPlaylistFetched(playlist_id) or forceRenew: self.updatePlaylistSongs(playlist_id) songs = self.storage.getPlaylistSongs(playlist_id) return songs def getPlaylistsByType(self, playlist_type, forceRenew=False): if forceRenew: self.updatePlaylists(playlist_type) playlists = self.storage.getPlaylistsByType(playlist_type) if len(playlists) == 0 and not forceRenew: self.updatePlaylists(playlist_type) playlists = self.storage.getPlaylistsByType(playlist_type) return playlists def getSong(self, song_id): return self.storage.getSong(song_id) def updatePlaylistSongs(self, playlist_id): api_songs = [] self.login.login() if playlist_id == 'all_songs': api_songs = self.gmusicapi.get_all_songs() else: api_songs = self.gmusicapi.get_playlist_songs(playlist_id) self.storage.storeApiSongs(api_songs, playlist_id) def updatePlaylists(self, playlist_type): self.login.login() playlists = self.gmusicapi.get_all_playlist_ids(playlist_type=="auto", playlist_type=="instant", playlist_type=="user", always_id_lists=True) self.storage.storePlaylists(playlists[playlist_type], playlist_type) def getSongStreamUrl(self, song_id): self.login.login() stream_url = self.gmusicapi.get_stream_url(song_id) #self.storage.updateSongStreamUrl(song_id, stream_url) return stream_url
class AlbumArtist(object): def __init__(self, email=None, password=None): self.api = Api() if not email: email = raw_input("Email: ") if not password: password = getpass() self.email = email self.password = password self.logged_in = self.auth() def auth(self): self.logged_in = self.api.login(self.email, self.password) if not self.logged_in: print "Login failed..." exit() print "" print "Logged in as %s" % self.email print "" def fix_album_artist(self): def noAlbumArtist(x): return len(x['albumArtist']) == 0 def fixSong(song): song['albumArtist'] = song['artist'] return song def chunks(l, n): """ Yield successive n-sized chunks from l. """ for i in xrange(0, len(l), n): yield l[i:i+n] songs = filter(noAlbumArtist, self.api.get_all_songs()) print "Found %d songs that have no albumArtist" % len(songs) fixedSongs = map(fixSong, songs) fixedSongChunks = chunks(fixedSongs, 100) map(self.api.change_song_metadata, fixedSongChunks) print "Fixed %d songs" % len(fixedSongs)
def importGmusicApi(email, password, gmusicCollection, keysCollection): api = Api() api.login(email, password) allSongs = api.get_all_songs() api.logout() gmusicCollection.remove() keys = set() for song in allSongs: gmusicCollection.insert(song) for key in song.iterkeys(): keys.add(key) doc = { '_id' : 'gmusic', 'keys' : list(keys) } keysCollection.save(doc) print 'done importing', len(allSongs), 'gmusic songs into db'
class GoogleMusicApi(): def __init__(self): self.storage = sys.modules["__main__"].storage self.gmusicapi = Api(debug_logging=False) self.login = GoogleMusicLogin.GoogleMusicLogin(self.gmusicapi) def getPlaylistSongs(self, playlist_id, forceRenew=False): if playlist_id == 'thumbsup': return self.storage.getThumbsup() if playlist_id == 'lastadded': return self.storage.getLastadded() if playlist_id == 'mostplayed': return self.storage.getMostplayed() if playlist_id == 'freepurchased': return self.storage.getFreepurchased() if not self.storage.isPlaylistFetched(playlist_id) or forceRenew: self.updatePlaylistSongs(playlist_id) songs = self.storage.getPlaylistSongs(playlist_id) return songs def getPlaylistsByType(self, playlist_type, forceRenew=False): if playlist_type == 'auto': return [['thumbsup', 'Highly Rated'], ['lastadded', 'Last Added'], ['freepurchased', 'Free and Purchased'], ['mostplayed', 'Most Played']] if forceRenew: self.updatePlaylists(playlist_type) playlists = self.storage.getPlaylistsByType(playlist_type) if len(playlists) == 0 and not forceRenew: self.updatePlaylists(playlist_type) playlists = self.storage.getPlaylistsByType(playlist_type) return playlists def getSong(self, song_id): return self.storage.getSong(song_id) def updatePlaylistSongs(self, playlist_id): api_songs = [] self.login.login() if playlist_id == 'all_songs': api_songs = self.gmusicapi.get_all_songs() else: api_songs = self.gmusicapi.get_playlist_songs(playlist_id) if api_songs: self.storage.storeApiSongs(api_songs, playlist_id) def updatePlaylists(self, playlist_type): self.login.login() playlists = self.gmusicapi.get_all_playlist_ids(playlist_type) self.storage.storePlaylists(playlists[playlist_type], playlist_type) def getSongStreamUrl(self, song_id): self.login.login() stream_url = self.gmusicapi.get_stream_url(song_id) #self.storage.updateSongStreamUrl(song_id, stream_url) return stream_url def getFilterSongs(self, filter_type, filter_criteria): songs = self.storage.getFilterSongs(filter_type, filter_criteria) return songs def getCriteria(self, criteria): return self.storage.getCriteria(criteria) def getSearch(self, query): return self.storage.getSearch(query) def clearCache(self): self.storage.clearCache() self.login.clearCookie() def clearCookie(self): self.login.clearCookie()
class Serenade(): def __init__(self): self.app = QtGui.QApplication(sys.argv) self.app.setApplicationName("Serenade") signal.signal(signal.SIGINT, signal.SIG_DFL) self.position = 0 self.player = MultimediaKit.QMediaPlayer(self.app) self.player.positionChanged.connect(self.positionChanged) self.signals = Signals() self.client = gconf.client_get_default() self.api = Api() self.artists = [] self.library = {} self.cacheDir = QtGui.QDesktopServices.storageLocation(QtGui.QDesktopServices.CacheLocation) if not os.path.exists(self.cacheDir): os.mkdir(self.cacheDir) self.artistModel = ArtistModel() self.songModel = SongModel() self.signals.onDoneWorking.connect(self.doneWorking) self.signals.onError.connect(self.error) self.view = QtDeclarative.QDeclarativeView() self.view.setSource("/opt/serenade/qml/Main.qml") self.rootObject = self.view.rootObject() self.context = self.view.rootContext() self.context.setContextProperty('songModel', self.songModel) self.context.setContextProperty('artistModel', self.artistModel) self.rootObject.openFile("MenuPage.qml") self.rootObject.refresh.connect(self.updateSongs) self.rootObject.newLogin.connect(self.newLogin) self.rootObject.play.connect(self.play) self.rootObject.togglePause.connect(self.togglePause) self.login() self.view.showFullScreen() sys.exit(self.app.exec_()) def login(self): email = self.client.get_string('/apps/serenade/email') passwd = self.client.get_string('/apps/serenade/passwd') if not email or not passwd: self.rootObject.openFile("LoginPage.qml") else: logged_in = self.api.login(email, passwd) if not logged_in: self.rootObject.openFile("LoginPage.qml") self.rootObject.showMessage("Login failed", "Please check your email and password and try again. If you're using two-factor authentication you may need to set an application specific password in your Google account.") else: self.rootObject.openFile("MenuPage.qml") self.updateSongs() def newLogin(self, email, passwd): self.client.set_string('/apps/serenade/email', email) self.client.set_string('/apps/serenade/passwd', passwd) self.login() def updateSongs(self): self.rootObject.startWorking() thread = threading.Thread(target=self._updateSongs) thread.start() def _updateSongs(self): self.library = sorted(self.api.get_all_songs(), key=lambda x: x['title']) self.library.reverse() for s in self.library: if "albumArtUrl" in s: art = self.getImage(s['albumArtUrl']) else: art = "/opt/serenade/no-art.png" if s['artist'] not in self.artists: artist = Artist(s['artist'], art) self.artists.append(s['artist']) self.artistModel.add(artist) song = Song(s['id'], s['title'], s['album'], s['artist'], False, art) self.songModel.add(song) self.artistModel.sort() self.signals.onDoneWorking.emit() def doneWorking(self): self.rootObject.stopWorking() def error(self, title, message): self.rootObject.showMessage(title, message) def getImage(self, url): filename = url.split("/")[-1] imagePath = os.path.join(self.cacheDir, filename) imagePath = imagePath.replace("?", "") if not os.path.exists(imagePath): try: out = open(imagePath, 'wb') out.write(urllib2.urlopen("http:" + url).read()) out.close() except Exception, err: return "/opt/serenade/no-art.png" return imagePath
class GoogleMusicApi(): def __init__(self): self.storage = sys.modules["__main__"].storage self.gmusicapi = Api(debug_logging=False) self.login = GoogleMusicLogin.GoogleMusicLogin(self.gmusicapi) def getPlaylistSongs(self, playlist_id, forceRenew=False): if playlist_id == 'thumbsup': return self.storage.getThumbsup() if playlist_id == 'lastadded': return self.storage.getLastadded() if playlist_id == 'mostplayed': return self.storage.getMostplayed() if playlist_id == 'freepurchased': return self.storage.getFreepurchased() if not self.storage.isPlaylistFetched(playlist_id) or forceRenew: self.updatePlaylistSongs(playlist_id) songs = self.storage.getPlaylistSongs(playlist_id) return songs def getPlaylistsByType(self, playlist_type, forceRenew=False): if playlist_type == 'auto': return [['thumbsup','Highly Rated'],['lastadded','Last Added'],['freepurchased','Free and Purchased'],['mostplayed','Most Played']] if forceRenew: self.updatePlaylists(playlist_type) playlists = self.storage.getPlaylistsByType(playlist_type) if len(playlists) == 0 and not forceRenew: self.updatePlaylists(playlist_type) playlists = self.storage.getPlaylistsByType(playlist_type) return playlists def getSong(self, song_id): return self.storage.getSong(song_id) def updatePlaylistSongs(self, playlist_id): api_songs = [] self.login.login() if playlist_id == 'all_songs': api_songs = self.gmusicapi.get_all_songs() else: api_songs = self.gmusicapi.get_playlist_songs(playlist_id) if api_songs: self.storage.storeApiSongs(api_songs, playlist_id) def updatePlaylists(self, playlist_type): self.login.login() playlists = self.gmusicapi.get_all_playlist_ids(playlist_type) self.storage.storePlaylists(playlists[playlist_type], playlist_type) def getSongStreamUrl(self, song_id): self.login.login() stream_url = self.gmusicapi.get_stream_url(song_id) #self.storage.updateSongStreamUrl(song_id, stream_url) return stream_url def getFilterSongs(self, filter_type, filter_criteria): songs = self.storage.getFilterSongs(filter_type, filter_criteria) return songs def getCriteria(self, criteria): return self.storage.getCriteria(criteria) def getSearch(self, query): return self.storage.getSearch(query) def clearCache(self): self.storage.clearCache() self.login.clearCookie() def clearCookie(self): self.login.clearCookie()
class GoogleMusic(): """This class handles the communication with Google Music.""" def __init__(self, email, password): if not email or not password: raise Exception("Username and password needs to be given") self._library = None self._artists = None self._albums = None self._playlists = None self._email = email self._password = password # Initialize the Google Music API self._api_init(self._email, self._password) if not self._api.is_authenticated(): raise Exception("Credentials were not accepted!") # Initial load of the library self.load_library() def _api_init(self, email, password): """Inits the API object and login to Google Music""" self._api = Api() self._api.login(email, password) def load_library(self): """ This function downloads the music library from Google Music and processes it. """ self._library = self._api.get_all_songs() # Generate artists and albums trees self._gen_trees() def _gen_trees(self): """ This function generates trees of artists and albums from the library. Parts of this function are taken from: https://github.com/mstill/thunner/blob/master/thunner => Thanks for the great code :) """ # Use defaultdict to group song dictionaries by artists artists_dict = collections.defaultdict(list) for i in self._library: artists_dict[i['artist']].append(i) artists = [] albums = [] for artist, songs_of_artist in artists_dict.iteritems(): albums_of_artists_dict = collections.defaultdict(list) for i in songs_of_artist: albums_of_artists_dict[i['album']].append(i) albums_of_artist = [] for album,tracks in albums_of_artists_dict.iteritems(): album_name = album if album == "": album_name = "Untitled album" albums_of_artist.append({ "name": album_name, "subtree": sorted(tracks, key=itemgetter('track')), "subtreeline": 0 }) albums = albums + albums_of_artist artists.append({ "name": artist, "subtree": sorted(albums_of_artist, key=lambda x: x['name'].lower()), "subtreeline": 0 }) self._artists = sorted(artists, key=lambda x: x['name'].lower()) self._albums = sorted(albums, key=lambda x: x['name'].lower()) def get_songs(self): """This function returns all songs from the music library.""" songs = [] for entry in self._library: song = { 'id': entry['id'], 'title': entry['title'], 'artist': entry['artist'], 'album': entry['album'] } songs.append(song) return songs def get_artists(self): pass def get_albums(self): pass def get_stream_url(self, id): return self._api.get_stream_url(id)
class MusicLibrary(object): 'Read information about your Google Music library' def __init__(self, username=None, password=None, true_file_size=False): self.__artists = {} # 'artist name' -> {'album name' : Album(), ...} self.__albums = [] # [Album(), ...] self.__login(username, password) self.__aggregate_albums() self.true_file_size = true_file_size def __login(self, username=None, password=None): # If credentials are not specified, get them from $HOME/.gmusicfs if not username or not password: cred_path = os.path.join(os.path.expanduser('~'), '.gmusicfs') if not os.path.isfile(cred_path): raise NoCredentialException( 'No username/password was specified. No config file could ' 'be found either. Try creating %s and specifying your ' 'username/password there. Make sure to chmod 600.' % cred_path) if not oct( os.stat(cred_path)[os.path.stat.ST_MODE]).endswith('00'): raise NoCredentialException( 'Config file is not protected. Please run: ' 'chmod 600 %s' % cred_path) config = ConfigParser.ConfigParser() config.read(cred_path) username = config.get('credentials', 'username') password = config.get('credentials', 'password') if not username or not password: raise NoCredentialException( 'No username/password could be read from config file' ': %s' % cred_path) self.api = GoogleMusicAPI() log.info('Logging in...') self.api.login(username, password, perform_upload_auth=False) log.info('Login successful.') def __aggregate_albums(self): 'Get all the tracks in the library, parse into artist and album dicts' all_artist_albums = {} # 'Artist|||Album' -> Album() log.info('Gathering track information...') tracks = self.api.get_all_songs() for track in tracks: # Get the Album object if it already exists: key = '%s|||%s' % (formatNames( track['artistNorm']), formatNames(track['albumNorm'])) album = all_artist_albums.get(key, None) if not album: # New Album artist = formatNames(track['artistNorm']) if artist == '': artist = 'unknown' album = all_artist_albums[key] = Album( self, formatNames(track['albumNorm'])) self.__albums.append(album) artist_albums = self.__artists.get(artist, None) if artist_albums: artist_albums[formatNames(album.normtitle)] = album else: self.__artists[artist] = {album.normtitle: album} artist_albums = self.__artists[artist] album.add_track(track) log.debug('%d tracks loaded.' % len(tracks)) log.debug('%d artists loaded.' % len(self.__artists)) log.debug('%d albums loaded.' % len(self.__albums)) def get_artists(self): return self.__artists def get_albums(self): return self.__albums def get_artist_albums(self, artist): log.debug(artist) return self.__artists[artist]
class MusicLibrary(object): 'Read information about your Google Music library' def __init__(self, username=None, password=None, true_file_size=False): self.__artists = {} # 'artist name' -> {'album name' : Album(), ...} self.__albums = [] # [Album(), ...] self.__login(username, password) self.__aggregate_albums() self.true_file_size = true_file_size def __login(self, username=None, password=None): # If credentials are not specified, get them from $HOME/.gmusicfs if not username or not password: cred_path = os.path.join(os.path.expanduser('~'), '.gmusicfs') if not os.path.isfile(cred_path): raise NoCredentialException( 'No username/password was specified. No config file could ' 'be found either. Try creating %s and specifying your ' 'username/password there. Make sure to chmod 600.' % cred_path) if not oct(os.stat(cred_path)[os.path.stat.ST_MODE]).endswith('00'): raise NoCredentialException( 'Config file is not protected. Please run: ' 'chmod 600 %s' % cred_path) config = ConfigParser.ConfigParser() config.read(cred_path) username = config.get('credentials','username') password = config.get('credentials','password') if not username or not password: raise NoCredentialException( 'No username/password could be read from config file' ': %s' % cred_path) self.api = GoogleMusicAPI() log.info('Logging in...') self.api.login(username, password, perform_upload_auth=False) log.info('Login successful.') def __aggregate_albums(self): 'Get all the tracks in the library, parse into artist and album dicts' all_artist_albums = {} # 'Artist|||Album' -> Album() log.info('Gathering track information...') tracks = self.api.get_all_songs() for track in tracks: # Get the Album object if it already exists: key = '%s|||%s' % (formatNames(track['artistNorm']), formatNames(track['albumNorm'])) album = all_artist_albums.get(key, None) if not album: # New Album artist = formatNames(track['artistNorm']) if artist == '': artist = 'unknown' album = all_artist_albums[key] = Album( self, formatNames(track['albumNorm'])) self.__albums.append(album) artist_albums = self.__artists.get(artist, None) if artist_albums: artist_albums[formatNames(album.normtitle)] = album else: self.__artists[artist] = {album.normtitle: album} artist_albums = self.__artists[artist] album.add_track(track) log.debug('%d tracks loaded.' % len(tracks)) log.debug('%d artists loaded.' % len(self.__artists)) log.debug('%d albums loaded.' % len(self.__albums)) def get_artists(self): return self.__artists def get_albums(self): return self.__albums def get_artist_albums(self, artist): log.debug(artist) return self.__artists[artist]
class GoogleMusic(): """This class handles the communication with Google Music.""" def __init__(self, email, password): if not email or not password: raise Exception("Username and password needs to be given") self._library = None self._artists = None self._albums = None self._playlists = None self._email = email self._password = password # Initialize the Google Music API self._api_init(self._email, self._password) if not self._api.is_authenticated(): raise Exception("Credentials were not accepted!") # Initial load of the library self.load_library() def _api_init(self, email, password): """Inits the API object and login to Google Music""" self._api = Api() self._api.login(email, password) def load_library(self): """ This function downloads the music library from Google Music and processes it. """ self._library = self._api.get_all_songs() # Generate artists and albums trees self._gen_trees() def _gen_trees(self): """ This function generates trees of artists and albums from the library. Parts of this function are taken from: https://github.com/mstill/thunner/blob/master/thunner => Thanks for the great code :) """ # Use defaultdict to group song dictionaries by artists artists_dict = collections.defaultdict(list) for i in self._library: artists_dict[i['artist']].append(i) artists = [] albums = [] for artist, songs_of_artist in artists_dict.iteritems(): albums_of_artists_dict = collections.defaultdict(list) for i in songs_of_artist: albums_of_artists_dict[i['album']].append(i) albums_of_artist = [] for album, tracks in albums_of_artists_dict.iteritems(): album_name = album if album == "": album_name = "Untitled album" albums_of_artist.append({ "name": album_name, "subtree": sorted(tracks, key=itemgetter('track')), "subtreeline": 0 }) albums = albums + albums_of_artist artists.append({ "name": artist, "subtree": sorted(albums_of_artist, key=lambda x: x['name'].lower()), "subtreeline": 0 }) self._artists = sorted(artists, key=lambda x: x['name'].lower()) self._albums = sorted(albums, key=lambda x: x['name'].lower()) def get_songs(self): """This function returns all songs from the music library.""" songs = [] for entry in self._library: song = { 'id': entry['id'], 'title': entry['title'], 'artist': entry['artist'], 'album': entry['album'] } songs.append(song) return songs def get_artists(self): pass def get_albums(self): pass def get_stream_url(self, id): return self._api.get_stream_url(id)
class GoogleMusicApi(): def __init__(self): self.xbmc = sys.modules["__main__"].xbmc self.xbmcgui = sys.modules["__main__"].xbmcgui self.xbmcplugin = sys.modules["__main__"].xbmcplugin self.settings = sys.modules["__main__"].settings self.language = sys.modules["__main__"].language self.dbg = sys.modules["__main__"].dbg self.common = sys.modules["__main__"].common self.storage = sys.modules["__main__"].storage self.gmusicapi = Api() self.login = GoogleMusicLogin.GoogleMusicLogin(self.gmusicapi) def getPlaylistSongs(self, playlist_id, forceRenew=False): if not self.storage.isPlaylistFetched(playlist_id) or forceRenew: self.updatePlaylistSongs(playlist_id) songs = self.storage.getPlaylistSongs(playlist_id) return songs def getPlaylistsByType(self, playlist_type, forceRenew=False): if forceRenew: self.updatePlaylists(playlist_type) playlists = self.storage.getPlaylistsByType(playlist_type) if len(playlists) == 0 and not forceRenew: self.updatePlaylists(playlist_type) playlists = self.storage.getPlaylistsByType(playlist_type) return playlists def getSong(self, song_id): return self.storage.getSong(song_id) def updatePlaylistSongs(self, playlist_id): api_songs = [] self.login.login() if playlist_id == 'all_songs': api_songs = self.gmusicapi.get_all_songs() else: api_songs = self.gmusicapi.get_playlist_songs(playlist_id) self.storage.storeApiSongs(api_songs, playlist_id) def updatePlaylists(self, playlist_type): self.login.login() playlists = self.gmusicapi.get_all_playlist_ids( playlist_type == "auto", playlist_type == "instant", playlist_type == "user", always_id_lists=True) self.storage.storePlaylists(playlists[playlist_type], playlist_type) def getSongStreamUrl(self, song_id): self.login.login() stream_url = self.gmusicapi.get_stream_url(song_id) #self.storage.updateSongStreamUrl(song_id, stream_url) return stream_url
class GMusicRater: def __init__(self, files): self.__api = Api() self.__by_rating = {} self.__needs_rating_update = [] # fill list of songs lib = [] cachefile = "gmusic.pickle" if os.path.isfile(cachefile): print "Loading cached library..." infile = open(cachefile, "rb") lib = cPickle.load(infile) else: self.__log_in() print "Getting music..." lib = self.__api.get_all_songs() print "Writing..." outfile = open(cachefile, "wb+") cPickle.dump(lib, outfile) # order list of songs by rating notfound = [] total_found = 0 for s in lib: r = files.find_rating(s) # error finding file: if r < 0: notfound.append(s) continue # print "Got rating for %s: %s" % (s["title"], s["rating"]) # file rating is different from cloud rating: if not r == s["rating"]: s["rating"] = r self.__needs_rating_update.append(s) if not self.__by_rating.has_key(r): self.__by_rating[r] = [] self.__by_rating[r].append(s) total_found += 1 print "Found %d cloud songs, %d of which need rating updates." % (total_found, len(self.__needs_rating_update)) print "Not found on disk: %d" % len(notfound) for s in notfound: print " %s - %s" % (s["artist"], s["title"]) def __log_in(self): if self.__api.is_authenticated(): return print "Logging in..." email = raw_input("Email: ") password = getpass.getpass() if not self.__api.login(email, password): print "couldnt log in" sys.exit(1) def reset_playlists(self): self.__log_in() playlists = self.__api.get_all_playlist_ids(auto=False, user=True)["user"] print "Got %d playlists:" % len(playlists) for k, v in playlists.iteritems(): print " Deleting %s (%s)" % (k, v) for playlistid in v: self.__api.delete_playlist(playlistid) def get_ids(slist): ret = [] for s in slist: ret.append(s["id"]) return ret awesome_songids = get_ids(self.__by_rating.get(5, [])) good_songids = awesome_songids + get_ids(self.__by_rating.get(4, [])) unrated_songids = get_ids(self.__by_rating.get(0, [])) awesome_pid = self.__api.create_playlist("Awesome") print "Awesome %s -> %d songs" % (awesome_pid, len(awesome_songids)) self.__api.add_songs_to_playlist(awesome_pid, awesome_songids) good_pid = self.__api.create_playlist("Good") print "Good %s -> %d songs" % (good_pid, len(good_songids)) self.__api.add_songs_to_playlist(good_pid, good_songids) unrated_pid = self.__api.create_playlist("Unrated") print "Unrated %s -> %d songs" % (unrated_pid, len(unrated_songids)) self.__api.add_songs_to_playlist(unrated_pid, unrated_songids) def update_ratings(self): total = len(self.__needs_rating_update) if total == 0: return self.__log_in() print "Updating %d songs..." % total # divide updates into chunks. start = 0 chunksz = 100 # could probably be larger, wasn't tested for max possible while start < total: end = start + chunksz if end >= total: end = total print "%d - %d" % (start, end) self.__api.change_song_metadata(self.__needs_rating_update[start:end]) start = end def logout(self): if self.__api.is_authenticated(): print "Logging out..." self.__api.logout()