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 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 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 MusicSync(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() print "Fetching playlists from Google..." self.playlists = self.api.get_all_playlist_ids(auto=False) print "Got %d playlists." % len(self.playlists['user']) print "" 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 sync_playlist(self, filename, remove_missing=False): filename = self.get_platform_path(filename) os.chdir(os.path.dirname(filename)) title = os.path.splitext(os.path.basename(filename))[0] print "Synching playlist: %s" % filename if title not in self.playlists['user']: print " didn't exist... creating..." self.playlists['user'][title] = [self.api.create_playlist(title)] print "" plid = self.playlists['user'][title][0] goog_songs = self.api.get_playlist_songs(plid) print "%d songs already in Google Music playlist" % len(goog_songs) local_songs = self.get_songs_from_playlist(filename) print "%d songs in local playlist" % len(local_songs) # Sanity check max 1000 songs per playlist if len(local_songs) > MAX_SONGS_IN_PLAYLIST: print " Google music doesn't allow more than %d songs in a playlist..." % MAX_SONGS_IN_PLAYLIST print " Will only attempt to sync the first %d songs." % MAX_SONGS_IN_PLAYLIST del local_songs[MAX_SONGS_IN_PLAYLIST:] existing_files = 0 added_files = 0 failed_files = 0 removed_files = 0 fatal_count = 0 for fn, song in local_songs: if self.file_already_in_list(song, goog_songs): existing_files += 1 continue print "" print "Adding: %s" % os.path.basename(fn) online = self.find_song(song) song_id = None if online: song_id = online['id'] print " already uploaded [%s]" % song_id else: if not os.path.isfile(fn): print " Attempted to upload non-existent file", song continue attempts = 0 result = [] while not result and attempts < MAX_UPLOAD_ATTEMPTS_PER_FILE: print " uploading... (may take a while)" attempts += 1 try: result = self.api.upload(fn) except (BadStatusLine, CannotSendRequest): # Bail out if we're getting too many disconnects if fatal_count >= MAX_CONNECTION_ERRORS_BEFORE_QUIT: print "" print "Too many disconnections - quitting. Please try running the script again." print "" exit() print "Connection Error -- Reattempting login" fatal_count += 1 self.api.logout() result = [] time.sleep(STANDARD_SLEEP) except: result = [] time.sleep(STANDARD_SLEEP) try: if result[0]: song_id = result[0].itervalues().next() else: song_id = result[1].itervalues().next() print " upload complete [%s]" % song_id except: print " upload failed - skipping" if not song_id: failed_files += 1 continue added = self.api.add_songs_to_playlist(plid, song_id) time.sleep(.3) # Don't spam the server too fast... print " done adding to playlist" added_files += 1 if remove_missing: for s in goog_songs: print "" print "Removing: %s" % s['title'] self.api.remove_songs_from_playlist(plid, s.id) time.sleep(.3) # Don't spam the server too fast... removed_files += 1 print "" print "---" print "%d songs unmodified" % existing_files print "%d songs added" % added_files print "%d songs failed" % failed_files print "%d songs removed" % removed_files def get_songs_from_playlist(self, filename): ext = os.path.splitext(filename)[1].lower() if ext == '.m3u': return list(self._get_songs_from_m3u_playlist(filename)) elif ext == '.xml': return list(self._get_songs_from_itunes_playlist(filename)) def _get_songs_from_m3u_playlist(self, filename): with codecs.open(filename, encoding='utf-8') as f: for line in f: line = line.rstrip().replace(u'\ufeff',u'') if line == "" or line[0] == "#": continue path = os.path.abspath(self.get_platform_path(line)) if not os.path.exists(path): print "File not found: %s" % line continue yield (path, self.get_id3_tag(path)) def _get_songs_from_itunes_playlist(self, filename): plist = plistlib.readPlist(filename) for track_id, track in plist['Tracks'].iteritems(): p = urlparse.urlparse(track['Location']) path = os.path.abspath(os.path.join(p.netloc, p.path)) yield (path, { 'name': track['Name'], #'artist': track['Artist'], 'album': track['Album'], 'track': track['Track Number'] if 'Track Number' in track else 0, 'disc': track['Disc Number'] if 'Disc Number' in track else 1 }) def file_already_in_list(self, song, goog_songs): i = 0 while i < len(goog_songs): if self.tag_compare(goog_songs[i], song): goog_songs.pop(i) return True i += 1 return False def get_id3_tag(self, filename): data = mutagen.File(filename, easy=True) r = {} if 'title' not in data: title = os.path.splitext(os.path.basename(filename))[0] print 'Found song with no ID3 title, setting using filename:' print ' %s' % title print ' (please note - the id3 format used (v2.4) is invisible to windows)' data['title'] = [title] data.save() r['name'] = data['title'][0] r['track'] = int(data['tracknumber'][0].split('/')[0]) if 'tracknumber' in data else 0 # If there is no track, try and get a track number off the front of the file... since thats # what google seems to do... # Not sure how google expects it to be formatted, for now this is a best guess if r['track'] == 0: m = re.match("(\d+) ", os.path.basename(filename)) if m: r['track'] = int(m.group(0)) r['artist'] = data['artist'][0] if 'artist' in data else '' r['album'] = data['album'][0] if 'album' in data else '' return r def find_song(self, song): results = self.api.search(song['name']) # NOTE - dianostic print here to check results if you're creating duplicates #print results['song_hits'] #print "%s ][ %s ][ %s ][ %s" % (tag['title'], tag['artist'], tag['album'], tag['track']) return find_in_list(results['song_hits'], song) def tag_compare(self, g_song, tag): # If a google result has no track, google doesn't return a field for it if 'track' not in g_song: g_song['track'] = 0 #g_song['artist'].lower() == tag['artist'].lower() and\ return g_song['name'].lower() == tag['name'].lower() and\ g_song['album'].lower() == tag['album'].lower() and\ g_song['track'] == tag['track'] def delete_song(self, sid): self.api.delete_songs(sid) print "Deleted song by id [%s]" % sid def get_platform_path(self, full_path): # Try to avoid messing with the path if possible if os.sep == '/' and '\\' not in full_path: return full_path if os.sep == '\\' and '\\' in full_path: return full_path if '\\' not in full_path: return full_path return os.path.normpath(full_path.replace('\\', '/'))
class 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