def __init__(self, hass, config): """Initialize the books authors.""" global GM_DEV_KEY global G_GM_MOBILE_CLIENT_API self.hass = hass self.all_gm_tracks = [] self.selected_books = [] _LOGGER.info("GM_USER: "******" GM_PASS: *******" + " GM_DEV_KEY: " + str(GM_DEV_KEY)) from gmusicapi import Mobileclient G_GM_MOBILE_CLIENT_API = Mobileclient() # if GM_DEV_KEY is None: # G_GM_MOBILE_CLIENT_API.login(GM_USER, GM_PASS, Mobileclient.FROM_MAC_ADDRESS) # else: # G_GM_MOBILE_CLIENT_API.login(GM_USER, GM_PASS, GM_DEV_KEY) # G_GM_MOBILE_CLIENT_API.oauth_login("3cf7d4cc166ab0ee") if not G_GM_MOBILE_CLIENT_API.is_authenticated(): _LOGGER.error("Failed to log in, check Google Music api") return False else: _LOGGER.info("OK - we are in Google Music") registered_devices = G_GM_MOBILE_CLIENT_API.get_registered_devices( )
def get_gmusic_playlists(username, password): api = Mobileclient() print(username + ":" + password) logged_in = api.login(username, password, Mobileclient.FROM_MAC_ADDRESS) if not logged_in: print("Login failed.") if api.is_authenticated(): playlists = api.get_all_user_playlist_contents() output_dict = {} for playlist in playlists: name = playlist["name"] tracks = playlist["tracks"] for track in tracks: track = track["track"] artist = track["artist"] title = track["title"] if name in output_dict: output_dict[name].append((artist, title)) else: output_dict[name] = [(artist, title)] return output_dict return None
def gpm_login(): client = Mobileclient() oauth = client.perform_oauth(open_browser=True) client.oauth_login(Mobileclient.FROM_MAC_ADDRESS, oauth_credentials = oauth, locale = 'en_US') if client.is_authenticated(): print('👌') return client
def Google_Auth(self): api = Mobileclient() attempts = 0 while not self.logged_in and attempts < 3: self.logged_in = api.login(self.username, self.password, self.devId) attempts += 1 if api.is_authenticated(): self.logged_in = True return api
def get_stream_client(self): device_id = self.get_device_id() client = Mobileclient(False, False, True) logger.info("Logging in '%s' with device id '%s'." % (self.username, device_id)) client.login(self.username, self.password, device_id) if not client.is_authenticated(): raise Exception("Client couldn't log in.") return client
class GPM_client: def __init__(self): logging.info("Starting GPM client") self.gpm_client = Mobileclient() # login while not self.gpm_client.is_authenticated(): logging.info("Logging you in...") if not self.gpm_client.oauth_login(device_id=Mobileclient.FROM_MAC_ADDRESS, oauth_credentials='./.gpmtoken'): logging.debug("No previous credentials - performing Oauth") self.gpm_client.perform_oauth(open_browser=True, storage_filepath='./.gpmtoken')
def get_authenitcated_client(): email = input("Email: ") password = getpass.getpass("Password: "******"Failied to authenticate, try again.") return get_authenitcated_client() return client
class GoogleMusic(object): def __init__(self): self.webclient = Webclient() self.mobileclient = Mobileclient() def is_authenticated(self): if not self.webclient.is_authenticated(): if self.mobileclient.is_authenticated(): return True return False def login(self, username, password): if not self.is_authenticated(): try: self.mobileclient.login(username, password, Mobileclient.FROM_MAC_ADDRESS) self.webclient.login(username, password) except Exception as e: raise Exception('Couldn\'t log into Google Music: ' + e.message) def search(self, query, kind): if self.is_authenticated(): results = self.mobileclient.search(query)[kind + '_hits'] return results def get_track(self, store_id): return self.mobileclient.get_track_info(store_id) def save_stream(self, track, destination): if self.is_authenticated(): with open(destination, 'w+b') as stream_file: url = self.mobileclient.get_stream_url(track.get('storeId')) stream_file.truncate(0) stream_file.seek(0, 2) audio = self.webclient.session._rsession.get(url).content stream_file.write(audio) tag = easyid3.EasyID3() tag['title'] = track.get('title').__str__() tag['artist'] = track.get('artist').__str__() tag['album'] = track.get('album').__str__() tag['date'] = track.get('year').__str__() tag['discnumber'] = track.get('discNumber').__str__() tag['tracknumber'] = track.get('trackNumber').__str__() tag['performer'] = track.get('albumArtist').__str__() tag.save(destination) tag = mp3.MP3(destination) tag.tags.add( id3.APIC(3, 'image/jpeg', 3, 'Front cover', urllib.urlopen(track.get('albumArtRef')[0].get('url')).read()) ) tag.save()
def get_google_playlists(): print("Retreiving playlists from Google Music.") playlists_path = f"{state_dir}/playlists.json" if os.path.exists(playlists_path) and not force_fetch: with open(playlists_path, 'r') as infile: return json.load(infile) print("Could not find saved favorites playlist, or force_fetch is True") credentials_path = f"{state_dir}/gmusic_credentials.json" mm = Mobileclient() if not os.path.exists(credentials_path): mm.perform_oauth(credentials_path, open_browser=True) mm.oauth_login(google_device_id, oauth_credentials=credentials_path) if mm.is_authenticated(): print("Authenticated sucessfully!") else: print("Could not authenticate :(") raise SystemExit(1) playlists = mm.get_all_user_playlist_contents() playlist_names = [p['name'] for p in playlists] print(f'Found playlists: {playlist_names}') clean_playlists = [] for p in playlists: playlist = { 'name': p['name'], 'tracks': [], } for track in p['tracks']: t = extract_google_track(track) if t is not None: playlist['tracks'].append(t) if len(playlist['tracks']) == 0: print(f"No tracks found in {p['name']}") else: clean_playlists.append(playlist) pprint(clean_playlists) if len(clean_playlists) == 0: print(f"No playlists with tracks found") raise SystemExit(1) with open(playlists_path, 'w') as outfile: json.dump(clean_playlists, outfile) return clean_playlists
class AudioStream: __username = configuration.get('google_username') __password = configuration.get('google_password') __track_prefetch = 15 __client = None __playlist = [] def __init__(self, station_id = 'IFL'): self.__client = Mobileclient() self.__client.login(self.__username, self.__password, Mobileclient.FROM_MAC_ADDRESS) self.__playlist = self.__fetchTrackIDs(station_id) def __del__(self): if self.__client: self.__client.logout() def __fetchTrackIDs(self, station_id): if not self.__client or not self.__client.is_authenticated(): logger.error("Client is not authenticated!") return [] tracklist = self.__client.get_station_tracks(station_id, num_tracks=self.__track_prefetch) logger.info("Received tracks: %r" % json.dumps(tracklist)) # Filter out explicit tracks, where non-explicit is explicitType=2 tracklist = [track for track in tracklist if not 'explicitType' in track or track['explicitType'] == "2"] logger.info("Non-explicit tracks: %r" % json.dumps(tracklist)) # Fetch both song IDs and Nautilus (old) IDs songids = [track['id'] for track in tracklist if 'id' in track] nautids = [track['nid'] for track in tracklist if 'nid' in track] return songids + nautids def pop(self): while self.__playlist: track_id = self.__playlist.pop() try: stream_url = self.__client.get_stream_url(track_id, quality='low') return stream_url except(exceptions.CallFailure): logger.warning("Failed to fetch Stream URL for ID %s" % track_id) raise IndexError("pop from empty list") def reverse(self): # Reverse just returns itself, since the playlist is already chaos return self def __len__(self): return len(self.__playlist)
class GPM_client: def __init__(self): logging.info("Starting GPM client") self.gpm_client = Mobileclient() # login if self.gpm_client.is_authenticated(): logging.info("Logging you in...") self.gpm_client.oauth_login( device_id=Mobileclient.FROM_MAC_ADDRESS) else: logging.debug("No previous credentials - performing Oauth") self.gpm_client.perform_oauth(open_browser=True)
def login_to_gmusic(username, password): """ DEPRECATED, use login_to_gmusic_with_oauth() instead params: username & password for your gmusic account returns the authenticated gmusic api object """ gm_api = Mobileclient() gm_api.login(email=username, password=password, \ android_id=gm_api.FROM_MAC_ADDRESS, locale=u'es_ES') if gm_api.is_authenticated(): print('Logged in to Google Music') return gm_api else: sys.stderr.write('error logging in, exiting program') sys.exit()
def __init__(self, hass, config): """Initialize the books authors.""" global G_GM_MOBILE_CLIENT_API self.hass = hass self.all_gm_tracks = [] self.selected_books = [] from gmusicapi import Mobileclient G_GM_MOBILE_CLIENT_API = Mobileclient() G_GM_MOBILE_CLIENT_API.login(GM_USER, GM_PASS, GM_DEV_KEY) if not G_GM_MOBILE_CLIENT_API.is_authenticated(): _LOGGER.error("Failed to log in, check gmusicapi") return False else: _LOGGER.info("OK - we are in Gmusic") _LOGGER.info("devices: " + str(G_GM_MOBILE_CLIENT_API.get_registered_devices()))
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)
def authorize_mobile(parameters): # api = Webclient() api = Mobileclient() # api = Musicmanager() # after running api.perform_oauth() once: # api.oauth_login('<a previously-registered device id>') # api.login(email=parameters.username, password=parameters.password) # => True if not os.path.exists(authorize_file): api.perform_oauth(authorize_file, True) api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, authorize_file) if not api.is_authenticated(): return None return api
def main(argv=None): """ Parse arguments, set up debugging and cache metadata. """ api = Mobileclient() parser_args = get_parser_args(argv) logging_args = { 'format': '%(asctime)s %(name)-12s %(levelname)-8s %(message)s', 'datefmt': '%m-%d %H:%M' } if parser_args.debug_level: logging_args['level'] = DEBUG_LEVELS[parser_args.debug_level] logging.basicConfig(**logging_args) for item, value in list(vars(parser_args).items()): if item == "pwd": continue logging.info("Parser arg: %15s = %s", item, value) if not os.path.exists(api.OAUTH_FILEPATH): logging.info("performing oauth") perform_oauth_args = {'open_browser': parser_args.oauth_browser} if parser_args.oauth_creds_file: perform_oauth_args[ 'storage_filepath'] = parser_args.oauth_creds_file api.perform_oauth(**perform_oauth_args) logging.info("logging in to api") response = api.oauth_login(device_id=parser_args.device_id) try: assert api.is_authenticated() logging.debug("valid device IDs: %s", pformat(api.get_registered_devices())) assert response except AssertionError: logging.warning("\n\n!!! failed to authenticate\n%s.", traceback.format_exc()) raise BadLoginException("Bad login. Check creds and internet") logging.info("api response: %s", response) cache_playlist(api, parser_args)
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)
def google_music_login(): ''' Ask for credentials and log into Google music. ''' sys.stdout.write("Connecting to Google Music ...\n") api = Mobileclient() logged_in = False attempts = 0 while not logged_in and attempts < 3: email = raw_input("Email: ") password = getpass() logged_in = api.login(email, password) attempts += 1 if not api.is_authenticated(): print("Login failed.", file=sys.stderr) return None sys.stdout.write("Successfully logged in.\n") return api
class PlayMusic: def __init__(self, google_username, google_password): self.client = Mobileclient(validate=False) # generate a stable, unique android id h = hashlib.sha256() h.update(google_username) android_id = h.hexdigest()[:16] self.client.login(google_username, google_password, android_id) def is_authenticated(self): return self.client.is_authenticated() @staticmethod def track_url(play_track): return "https://play.google.com/music/m/%s" % play_track["nid"] @staticmethod def playlist_url(playlist_id): return "https://play.google.com/music/playlist/%s" % playlist_id def search_tracks(self, query): if len(query) >= MAX_QUERY_LENGTH: # long queries don't work for some reason # for example "The Moderately Talented Yet Attractive Young Woman vs. The Exceptionally Talented Yet Not # So Attractive Middle Aged Man / Sun Kil Moon / Among The Leaves" parts = query.split(" ") query = parts.pop(0) for part in parts: if len(query) + 1 + len(part) > MAX_QUERY_LENGTH: break query += " " + part retries = RETRIES response = None while retries and not response: retries -= 1 try: response = self.client.search_all_access(query) except Exception, e: if not retries: raise e # sleep for two seconds before retrying time.sleep(2) return [PlayTrack(song["track"]) for song in response["song_hits"]]
class PlayMusic(): def __init__(self, google_username, google_password): self.client = Mobileclient(validate=False) # generate a stable, unique android id h = hashlib.sha256() h.update(google_username) android_id = h.hexdigest()[:16] self.client.login(google_username, google_password, android_id) def is_authenticated(self): return self.client.is_authenticated() @staticmethod def track_url(play_track): return 'https://play.google.com/music/m/%s' % play_track['nid'] @staticmethod def playlist_url(playlist_id): return 'https://play.google.com/music/playlist/%s' % playlist_id def search_tracks(self, query): if len(query) >= MAX_QUERY_LENGTH: # long queries don't work for some reason # for example "The Moderately Talented Yet Attractive Young Woman vs. The Exceptionally Talented Yet Not # So Attractive Middle Aged Man / Sun Kil Moon / Among The Leaves" parts = query.split(' ') query = parts.pop(0) for part in parts: if len(query) + 1 + len(part) > MAX_QUERY_LENGTH: break query += ' ' + part retries = RETRIES response = None while retries and not response: retries -= 1 try: response = self.client.search_all_access(query) except Exception, e: if not retries: raise e # sleep for two seconds before retrying time.sleep(2) return [PlayTrack(song['track']) for song in response['song_hits']]
def __init__(self, api: Mobileclient, hotkey_mgr: SystemHotkey, library: Library): self.initialized_val = Atomic(False) self.api = api self.hotkey_mgr = hotkey_mgr self.library = library if self.hotkey_mgr: self.setup_hotkeys() # List of track ids self.tracks_to_play = [] self.current_track_index = Atomic(None) self.current_song_info = Atomic(None) self.player = None self.pending_events = [] self.event_handler_thread = None if api.is_authenticated(): self.initialize()
def login_google(): """ Log into Google and retrieve user library and playlists """ g = Mobileclient() logged_in = g.login(config.auth['GOOGLE_EMAIL'], config.auth['GOOGLE_PASSWORD'], Mobileclient.FROM_MAC_ADDRESS) if not g.is_authenticated(): log.error("Invalid Google email/password; exiting.") sys.exit(1) log.info("Retrieving Google Music playlists") g.playlists = g.get_all_user_playlist_contents() log.info("Retrieving Google Music library") g.library = get_google_library(g) return g
def __init__(self, login=True, dry_run=False): if login: try: google_username = os.environ.get('USERNAME') google_password = os.environ.get('PASSWORD') api = Mobileclient() api.login(google_username, google_password, Mobileclient.FROM_MAC_ADDRESS) if api.is_authenticated(): self.api = api except: print "ERROR: Unable to login with the credentials provided!" sys.exit(1) if dry_run: print "Dry-run mode enabled. Logging only. No changes will be made." self.dry_run = True else: self.dry_run = False
def get_library(mobile_client: Mobileclient) -> List[GpmTrack]: """ Given an authenticated mobile client, return a list of GPM Tracks representing the users library Args: mobile_client (Mobileclient): Must be authenticated. Client is used to retrieve the library for the GPM user. Returns: List[GpmTrack]: A list of ``GpmTrack`` objects representing the user's library. """ # Check if client isn't authenticated if not mobile_client.is_authenticated(): raise UnauthenticatedClientException("Trying to get library with an unauthenticated mobile client") # Get the library as a list of dicts raw_tracks: List[dict] = mobile_client.get_all_songs() return [ApiWrapper.__map_dict_to_gpm_track(track) for track in raw_tracks]
def get_google(): global google_client if google_client is not None: return google_client credentials_path = f"{state_dir}/gmusic_credentials.json" google_client = Mobileclient() # mm.perform_oauth() if not os.path.exists(credentials_path): google_client.perform_oauth(credentials_path, open_browser=True) google_client.oauth_login(google_device_id, oauth_credentials=credentials_path) if google_client.is_authenticated(): print("Authenticated sucessfully!") else: print("Could not authenticate :(") raise SystemExit(1) return google_client
def play_song_by_artist(song, artist): if Mobileclient.is_authenticated(gpm): mm = Musicmanager() mm.login('/home/pi/oauth.cred') if Musicmanager.is_authenticated(mm): song_dict = mm.get_purchased_songs() song_pattern = re.compile( r'(?:.)*\s?(' + re.escape(song) + r')\s?(?:.)*', re.IGNORECASE) artist_pattern = re.compile( r'(?:.)*\s?(' + re.escape(artist) + r')\s?(?:.)*', re.IGNORECASE) btn = OnButtonPress() btn.start() for song in song_dict: m = re.match(artist_pattern, song['artist']) print(m) if (re.match(song_pattern, song['title']) is not None and re.match(artist_pattern, song['artist']) is not None): print('Song found!') song_id = song['id'] (filename, audio) = mm.download_song(song_id) # get rid of non-ascii characters in file name filename = filename.encode('ascii', errors='ignore') # check if song is already downloaded # path will look something like: # /home/pi/Music/02 - Raindrop Prelude.mp3 # forces filename to be a string filename = filename.decode('ascii') path = song_location + filename try: if os.path.isfile(path): print('Song is already downloaded...') print(path) print('Playing song.') vlc_instance = vlc.Instance() p = vlc_instance.media_player_new() media = vlc_instance.media_new(path) p.set_media(media) events = p.event_manager() events.event_attach( vlc.EventType.MediaPlayerEndReached, SongFinished) p.play() p.audio_set_volume(58) while finish == 0: duration = p.get_time() / 1000 (m, s) = divmod(duration, 60) print('Current song is: ', path) print('Length:', '%02d:%02d' % (m, s)) time.sleep(5) p.stop() break else: with open(path, 'wb') as f: f.write(audio) print('Song has been added to: ' + path) print('Playing song.') vlc_instance = vlc.Instance() p = vlc_instance.media_player_new() media = vlc_instance.media_new(path) p.set_media(media) events = p.event_manager() events.event_attach( vlc.EventType.MediaPlayerEndReached, SongFinished) p.play() p.audio_set_volume(58) while finish == 0: duration = p.get_time() / 1000 m, s = divmod(duration, 60) print('Current song is: ', path) print('Length:', '%02d:%02d' % (m, s)) time.sleep(5) p.stop() break except (OSError, IOError): print('An error has occurred.') break else: print('Song not found yet.') else: print('Looks like you need to authenticate.') mm.perform_oauth('/home/pi/oauth.cred') print('Logging out.') Mobileclient.logout(gpm) mm.logout() else: print('Mobile client is not authenticated.') Mobileclient.logout(gpm)
class IBCMusicClient(): # Please have this be an absolute path SONG_DIR = "/home/pi/Desktop/JukeSite/songs" def __init__(self): self.api = None self.player = None def start(self): """ Starts the MobileClient """ self.api = Mobileclient() def stop(self): """ Deletes MobileClient and sets self.api to default(None) """ del self.api self.api = None def logon(self, email, password): """ Logs onto google music as a mobile client. Returns true is sucessful. :param email: Email of the Google user :param password: Pass of the google user :return: Bool if connection was successful """ if self.api is None: raise errors.MobileClientNotInitError( "The Client has not been init therefor it cannot logon.", 1000) try: res = self.api.login(email, password, self.api.FROM_MAC_ADDRESS) except AlreadyLoggedIn as e: self.api.logout() res = self.api.login(email, password, self.api.FROM_MAC_ADDRESS) del email del password return res def logout(self): """ logs out of google music mobile client. :return: if it was succesful """ return self.api.logout() def is_authenticated(self): if not self.api.is_authenticated(): raise errors.SessionNotActive( "The session is no longer active. Either it timedout or you have not logged in", 1001) def search_song(self, query): """ Searchs for the given query and return the song results Will check for authentication. [{ 'track': { 'album': 'Work Out', 'albumArtRef': [{ 'aspectRatio': '1', 'autogen': False, 'kind': 'sj#imageRef', 'url': 'http://lh5.ggpht.com/DVIg4GiD6msHfgPs_Vu_2eRxCyAoz0fFdxj5w...' }], 'albumArtist': 'J.Cole', 'albumAvailableForPurchase': True, 'albumId': 'Bfp2tuhynyqppnp6zennhmf6w3y', 'artist': 'J Cole', 'artistId': ['Ajgnxme45wcqqv44vykrleifpji', 'Ampniqsqcwxk7btbgh5ycujij5i'], 'composer': '', 'discNumber': 1, 'durationMillis': '234000', 'estimatedSize': '9368582', 'explicitType': '1', 'genre': 'Pop', 'kind': 'sj#track', 'nid': 'Tq3nsmzeumhilpegkimjcnbr6aq', 'primaryVideo': { 'id': '6PN78PS_QsM', 'kind': 'sj#video', 'thumbnails': [{ 'height': 180, 'url': 'https://i.ytimg.com/vi/6PN78PS_QsM/mqdefault.jpg', 'width': 320 }] }, 'storeId': 'Tq3nsmzeumhilpegkimjcnbr6aq', 'title': 'Work Out', 'trackAvailableForPurchase': True, 'trackAvailableForSubscription': True, 'trackNumber': 1, 'trackType': '7', 'year': 2011 }, 'type': '1' }] :param query: The song query :return: [list] all the song hits """ self.is_authenticated() res = self.api.search(query) songs = res['song_hits'] return songs def search_album(self, query): """ Searchs for the given query and returns the album results. Will check for authenitcation. e.g return: [{ 'album': { 'albumArtRef': 'http://lh5.ggpht.com/DVIg4GiD6msHfgPs_Vu_2eRxCyAoz0fF...', 'albumArtist': 'J.Cole', 'albumId': 'Bfp2tuhynyqppnp6zennhmf6w3y', 'artist': 'J.Cole', 'artistId': ['Ajgnxme45wcqqv44vykrleifpji'], 'description_attribution': { 'kind': 'sj#attribution', 'license_title': 'Creative Commons Attribution CC-BY', 'license_url': 'http://creativecommons.org/licenses/by/4.0/legalcode', 'source_title': 'Freebase', 'source_url': '' }, 'explicitType': '1', 'kind': 'sj#album', 'name': 'Work Out', 'year': 2011 }, 'type': '3' }] :param query: [string] The album query :return: [list] A list of all the album hits """ self.is_authenticated() res = self.api.search(query) albums = res['album_hits'] return albums def get_album_info(self, album_id): """ Returns information about an album e.g return: { 'kind': 'sj#album', 'name': 'Circle', 'artist': 'Amorphis', 'albumArtRef': 'http://lh6.ggpht.com/...', 'tracks': [ # if `include_tracks` is True { 'album': 'Circle', 'kind': 'sj#track', 'storeId': 'T5zb7luo2vkroozmj57g2nljdsy', # can be used as a song id 'artist': 'Amorphis', 'albumArtRef': [ { 'url': 'http://lh6.ggpht.com/...' }], 'title': 'Shades of Grey', 'nid': 'T5zb7luo2vkroozmj57g2nljdsy', 'estimatedSize': '13115591', 'albumId': 'Bfr2onjv7g7tm4rzosewnnwxxyy', 'artistId': ['Apoecs6off3y6k4h5nvqqos4b5e'], 'albumArtist': 'Amorphis', 'durationMillis': '327000', 'composer': '', 'genre': 'Metal', 'trackNumber': 1, 'discNumber': 1, 'trackAvailableForPurchase': True, 'trackType': '7', 'albumAvailableForPurchase': True }, # ... ], 'albumId': 'Bfr2onjv7g7tm4rzosewnnwxxyy', 'artistId': ['Apoecs6off3y6k4h5nvqqos4b5e'], 'albumArtist': 'Amorphis', 'year': 2013 } :param album_id: The albumId :return: Dictionary in the format above """ self.is_authenticated() return self.api.get_album_info(album_id) def get_song_info(self, song_id): """ Returns information about a song e.g return { 'album': 'Best Of', 'kind': 'sj#track', 'storeId': 'Te2qokfjmhqxw4bnkswbfphzs4m', 'artist': 'Amorphis', 'albumArtRef': [ { 'url': 'http://lh5.ggpht.com/...' }], 'title': 'Hopeless Days', 'nid': 'Te2qokfjmhqxw4bnkswbfphzs4m', 'estimatedSize': '12325643', 'albumId': 'Bsbjjc24a5xutbutvbvg3h4y2k4', 'artistId': ['Apoecs6off3y6k4h5nvqqos4b5e'], 'albumArtist': 'Amorphis', 'durationMillis': '308000', 'composer': '', 'genre': 'Metal', 'trackNumber': 2, 'discNumber': 1, 'trackAvailableForPurchase': True, 'trackType': '7', 'albumAvailableForPurchase': True } :param song_id: The songds storeId :return: A dict with the above information """ self.is_authenticated() return self.api.get_track_info(song_id) def get_song_url(self, song_id): self.is_authenticated() res = self.api.get_stream_url(song_id) return res def download_song(self, song_id): """ Download the song from the storeId. :param song_id: the 'storeId' of the specific song """ url = self.get_song_url(song_id) song_file_path = "{}/{}.mp3".format(IBCMusicClient.SONG_DIR, song_id) if os.path.isfile(song_file_path): raise errors.SongAlreadyDownloadedException( "The song '{}' has already been downloaded and cached".format( song_file_path), 8002) # This need to not use subprocessing command = ['wget', url, '-O', song_file_path] res = check_output(command) lines = res.decode().split('\n') error_lines = [line for line in lines if 'failed' in line] if len(error_lines) > 0: # We have an error raise errors.CannotDownloadSongError( "Could not download the given song. {}".format( str(error_lines)), 1003)
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
try: jfile = open(hidden_vals) except: print( 'Need a file named ajsonhidden_vals.json in the current working directory with login information.' ) sys.exit() jstr = jfile.read() jdata = json.loads(jstr) gaccount = jdata['Gmusic login']['account'] gapi = jdata['Gmusic login']['api_key'] api.login(gaccount, gapi, Mobileclient.FROM_MAC_ADDRESS) # => True print(api.is_authenticated()) def gettracks(artistId, existing_nids): recentalbum = [] try: results = api.get_artist_info(artistId, include_albums=True, max_top_tracks=20, max_rel_artist=5) albums = results['albums'] for a in albums: if (str(a['year']) == '2017'): recentalbum.append(a) except Exception as e: print('Couldnt get an album')
oauth_credentials=credentials_filepath) except Exception as e: valid_devices = e.valid_device_ids #Complete the login process just using the first returned id, or this machine's MAC address if no id is returned. if len(valid_devices) > 0: mc.oauth_login(device_id=valid_devices[0], oauth_credentials=credentials_filepath) else: print( "No device ids found, attempting to use this machine's MAC address...") mc.oauth_login(device_id=Mobileclient.FROM_MAC_ADDRESS, oauth_credentials=credentials_filepath) #Ensure the device is is authenticated (it probably is, since we were just able to login, but can't hurt to be explicit) if mc.is_authenticated(): print("Connected Successfully!") else: 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()
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)
class GoogleMusic(object): def __init__(self): self.webclient = Webclient() self.mobileclient = Mobileclient() def is_authenticated(self): if self.webclient.is_authenticated(): if self.mobileclient.is_authenticated(): return True return False def login(self, username, password): if not self.is_authenticated(): try: self.mobileclient.login(username, password) self.webclient.login(username, password) except: raise Exception('Couldn\'t log into Google Music') def search(self, query, kind): if self.is_authenticated(): results = self.mobileclient.search_all_access(query)[kind + '_hits'] return results def get_track(self, store_id): return self.mobileclient.get_track_info(store_id) def save_stream(self, track, destination): if self.is_authenticated(): with open(destination, 'w+b') as stream_file: urls = self.webclient.get_stream_urls(track.get('storeId')) if len(urls) == 1: stream_file.write(self.webclient.session._rsession.get(urls[0]).content) range_pairs = [[int(s) for s in val.split('-')] for url in urls for key, val in parse_qsl(urlparse(url)[4]) if key == 'range'] for url, (start, end) in zip(urls, range_pairs): stream_file.truncate(start) stream_file.seek(0, 2) audio = self.webclient.session._rsession.get(url).content stream_file.write(audio) tag = easyid3.EasyID3() tag['title'] = track.get('title').__str__() tag['artist'] = track.get('artist').__str__() tag['album'] = track.get('album').__str__() tag['date'] = track.get('year').__str__() tag['discnumber'] = track.get('discNumber').__str__() tag['tracknumber'] = track.get('trackNumber').__str__() tag['performer'] = track.get('albumArtist').__str__() tag.save(destination) tag = mp3.MP3(destination) tag.tags.add( id3.APIC(3, 'image/jpeg', 3, 'Front cover', urllib.urlopen(track.get('albumArtRef')[0].get('url')).read()) ) tag.save()
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)
from mutagen.mp3 import MP3 import mutagen.id3 EasyID3.RegisterTextKey("albumartist", "TPE2") EasyID3.RegisterTextKey("media", "TMED") r_albumID = 'Bmzku73mo3yicub2vnes43wv52y' api = Mobileclient() try: api.login(gMusicLogin.getUser(), gMusicLogin.getPass(), Mobileclient.FROM_MAC_ADDRESS) except: sys.exit("Login Failed") while api.is_authenticated() == False: time.sleep(.1) def removeNonASCII(text): return ''.join(i for i in text if ord(i) < 128) def runMainBody(): r_album = api.get_album_info(r_albumID) w_Album = {} w_Album['name'] = r_album['name'] print(w_Album['name']) w_Album['songs'] = []
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()
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)
class GoogleMusicLogin(): def __init__(self): import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning from requests.packages.urllib3.exceptions import InsecurePlatformWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) requests.packages.urllib3.disable_warnings(InsecurePlatformWarning) self.gmusicapi = Mobileclient(debug_logging=False, validate=False, verify_ssl=False) def checkCookie(self): # Remove cookie data if it is older then 7 days if utils.addon.getSetting('cookie-date') != None and len(utils.addon.getSetting('cookie-date')) > 0: import time if (datetime.now() - datetime(*time.strptime(utils.addon.getSetting('cookie-date'), '%Y-%m-%d %H:%M:%S.%f')[0:6])).days >= 7: self.clearCookie() def checkCredentials(self): if not utils.addon.getSetting('username'): utils.addon.openSettings() if utils.addon.getSetting('password') and utils.addon.getSetting('password') != '**encoded**': import base64 utils.addon.setSetting('encpassword',base64.b64encode(utils.addon.getSetting('password'))) utils.addon.setSetting('password','**encoded**') def getApi(self): return self.gmusicapi def getStreamUrl(self,song_id): # retrieve registered device device_id = self.getDevice() # retrieve stream quality from settings quality = { '0':'hi','1':'med','2':'low' } [utils.addon.getSetting('quality')] utils.log("getStreamUrl songid: %s device: %s quality: %s"%(song_id, device_id, quality)) return self.gmusicapi.get_stream_url(song_id, device_id, quality) def getDevice(self): return utils.addon.getSetting('device_id') def initDevice(self): device_id = self.getDevice() if not device_id: utils.log('Trying to fetch the device_id') self.login(True) try: devices = self.gmusicapi.get_registered_devices() if len(devices) == 10: utils.log("WARNING: 10 devices already registered!") utils.log(repr(devices)) for device in devices: if device["type"] in ("ANDROID","PHONE","IOS"): device_id = str(device["id"]) break except: pass if device_id: if device_id.lower().startswith('0x'): device_id = device_id[2:] utils.addon.setSetting('device_id', device_id) utils.log('Found device_id: '+device_id) else: #utils.log('No device found, using default.') #device_id = "333c60412226c96f" raise Exception('No devices found, registered mobile device required!') def clearCookie(self): utils.addon.setSetting('logged_in-mobile', "") utils.addon.setSetting('authtoken-mobile', "") utils.addon.setSetting('device_id', "") def logout(self): self.gmusicapi.logout() def login(self, nocache=False): if not utils.addon.getSetting('logged_in-mobile') or nocache: import base64 utils.log('Logging in') self.checkCredentials() username = utils.addon.getSetting('username') password = base64.b64decode(utils.addon.getSetting('encpassword')) try: self.gmusicapi.login(username, password, utils.addon.getSetting('device_id')) if not self.gmusicapi.is_authenticated(): self.gmusicapi.login(username, password, Mobileclient.FROM_MAC_ADDRESS) except Exception as e: utils.log(repr(e)) if not self.gmusicapi.is_authenticated(): utils.log("Login failed") utils.addon.setSetting('logged_in-mobile', "") self.language = utils.addon.getLocalizedString dialog = xbmcgui.Dialog() dialog.ok(self.language(30101), self.language(30102)) #utils.addon.openSettings() raise else: utils.log("Login succeeded") utils.addon.setSetting('logged_in-mobile', "1") utils.addon.setSetting('authtoken-mobile', self.gmusicapi.session._authtoken) utils.addon.setSetting('cookie-date', str(datetime.now())) else: utils.log("Loading auth from cache") self.gmusicapi.session._authtoken = utils.addon.getSetting('authtoken-mobile') self.gmusicapi.session.is_authenticated = True
class Downloader: def __init__( self , username1=None , password1=None , baseDirectory=None , pickleLIBFilePath=None ): self.api = Mobileclient() if username1 is not None: if password1 is not None: self.login( username1 , password1 ) if self.isLoggedIn() == True: print("Logged In") else: raise Exception # Setup Default Save Location if baseDirectory is not None: self.homeDIR = baseDirectory self.libDIR = os.path.join( self.homeDIR , 'GMusicLocalLibraryPOOL' ) else: self.homeDIR = os.path.expanduser("~") self.libDIR = os.path.join( self.homeDIR , 'GMusicLocalLibraryPOOL' ) if not os.path.exists(self.libDIR): os.makedirs(self.libDIR) self.stations = {} self.workingPlaylistOBJ = {} self.needToDownloadSongs = None self.Full = True #self.playlists = None self.localLibrary = None #self.initializePlaylists() if pickleLIBFilePath is not None: self.initializeLocalLibrary(pickleLIBFilePath) else: self.initializeLocalLibrary() def isLoggedIn(self): x = self.api.is_authenticated() return x def login(self , userN , passW ): self.api.login( userN , passW , Mobileclient.FROM_MAC_ADDRESS ) def initializePlaylists(self): try: self.playlists = pickle.load( open( libDIR + "libPlaylists.p" , "rb" ) ) except: print("Recreating Playlists Save File") self.playlists = {} playlists['EDM'] = [] playlists['Relaxing'] = [] playlists['EDM'].append('4b40425b-2e11-388f-aeed-ea736b88662c') pickle.dump( playlists , open( libDIR + "libPlaylists.p" , "wb" ) ) def initializeLocalLibrary(self , pickleLIBFilePath=None): defaultPath2 = os.path.join( self.libDIR , "libDatabasePOOL.p" ) if pickleLIBFilePath is not None: defaultPath2 = pickleLIBFilePath print("DefaultPath .p file = " + defaultPath2) try: self.localLibrary = pickle.load( open( defaultPath2 , "rb" ) ) print("Loaded libDatabasePOOL.p") except: self.localLibrary = {} pickle.dump( self.localLibrary , open( defaultPath2 , "wb" ) ) print("Recreated LibraryPOOL Save File") print( "LocalLibary Size = " + str( len( self.localLibrary ) ) ) def getMyStations(self): stations = self.api.get_all_stations() for x in stations: self.stations[x['id']] = x['name'] def printAvailableStations(self): for x in self.stations: print( str(x) + " = " + self.stations[x] ) def downloadStationToPOOL( self , stationID ): rawPlaylist = self.api.get_station_tracks( stationID , 25 ) self.needToDownloadSongs = {} for x in rawPlaylist: if x['nid'] in self.localLibrary: print("Already in LibraryPOOL") else: self.Full = False print( str(x['nid']) + " == Not in library ... need to download" ) self.needToDownloadSongs[x['nid']] = { 'stationID': stationID , 'trackName': x['title'] , 'artistName': x['artist'] , 'albumID': x['albumId'] , 'artURL': x['albumArtRef'][0]['url'] } p1 = threading.Thread( target=self.getMP3FromSongIDS , args=( stationID , ) ) p1.start() p1.join() if self.Full == False: self.Full = True print( "LocalLibary Size = " + str( len( self.localLibrary ) ) ) self.downloadStationToPOOL( stationID ) def getMP3FromSongIDS( self , stationID ): a1 = 1 for x in self.needToDownloadSongs: self.saveMP3ToLibraryPOOL( x , self.needToDownloadSongs[x]['trackName'] , self.needToDownloadSongs[x]['artistName'] , stationID ) self.localLibrary[x] = self.needToDownloadSongs[x] pickle.dump( self.localLibrary , open( os.path.join( self.libDIR , "libDatabasePOOL.p" ) , "wb" ) ) print("added [" + str(a1) + " of " + str(len(self.needToDownloadSongs)) + "] songs to localLibrary") a1 = a1 + 1 def saveMP3ToLibraryPOOL( self , songID , name , artist , stationID ): albumName = self.stations[stationID] wURL = self.api.get_stream_url( songID , self.api.android_id , 'hi' ) fN = os.path.join( self.libDIR , songID + ".mp3" ) response1 = requests.get( wURL , stream=True ) with open( fN , 'wb' ) as f: for data in tqdm( response1.iter_content(chunk_size=524288) ): f.write(data) m3 = MP3( fN , ID3=EasyID3 ) m3.add_tags( ID3=EasyID3 ) m3["title"] = name m3['artist'] = artist m3['album'] = albumName m3['organization'] = stationID m3.save() def extractSinglePlaylistFromPOOL( self , stationID , destinationDIR , onlyCopyNotExtract=False ): if not os.path.exists(destinationDIR): os.makedirs(destinationDIR) if onlyCopyNotExtract == True: for key , value in self.localLibrary.items(): try: if value['stationID'] == stationID: #print( "found --> " + self.localLibrary[key]['trackName'] + " in ... " + str(stationID) ) fN = str(key) + ".mp3" shutil.copy( os.path.join( self.libDIR , fN ) , os.path.join( destinationDIR , fN ) ) except: pass else: for key , value in self.localLibrary.items(): try: if value['stationID'] == stationID: #print( "found --> " + self.localLibrary[key]['trackName'] + " in ... " + str(stationID) ) fN = str(key) + ".mp3" shutil.move( os.path.join( self.libDIR , fN ) , os.path.join( destinationDIR , fN ) ) except: pass
import gMusicLogin from tqdm import tqdm import sys , os , time , requests from mutagen.easyid3 import EasyID3 from mutagen.mp3 import MP3 import mutagen.id3 api = Mobileclient() try: api.login( gMusicLogin.getUser() , gMusicLogin.getPass() , Mobileclient.FROM_MAC_ADDRESS ) except: sys.exit("Login Failed") while api.is_authenticated() == False: time.sleep(.1) wPlaylists = None def removeNonASCII(text): return ''.join(i for i in text if ord(i)<128) def getPlaylists(): global wPlaylists wPlaylists = api.get_all_playlists() for idx , x in enumerate( wPlaylists ): x['name'] = removeNonASCII( x['name'] ) print( str( idx ) + " = " + x['name'] ) def getSelection():
def main(): if len(sys.argv) != 3: print_help() sys.exit(1) else: username = sys.argv[1] path = sys.argv[2] password = getpass.getpass() pp = pprint.PrettyPrinter(indent=4) local_list = get_local_dirs(path) mob = Mobileclient() mob.login(username, password, Mobileclient.FROM_MAC_ADDRESS) if not mob.is_authenticated(): sys.exit(1) mmw = MusicManagerWrapper(enable_logging=True) mmw.login() if not mmw.is_authenticated: sys.exit() total_items = 0 partial_accepted_items = 0 partial_rejected_items = 0 partial_manual_items = 0 exact_items = 0 no_items = 0 ACCEPT_RATIO = 0.33 REJECT_RATIO = 0.66 matched_albums = [] unmatched_albums = [] manual_albums = [] for item in local_list: search_artist = item['artist'] for search_album in item['albums']: total_items += 1 albums = search_for_artist_and_album(mob, search_artist, search_album) sorted_albums = sorted(albums, key=lambda k:k[2]) if len(sorted_albums) > 0: (artist, album, ratio, album_id) = sorted_albums[0] if ratio > 0: if ratio < ACCEPT_RATIO: partial_accepted_items += 1 partial_description = 'Partial Match (Accepted)' matched_albums.append((artist, album, album_id)) elif ratio > REJECT_RATIO: partial_rejected_items += 1 partial_description = 'Partial Match (Rejected)' unmatched_albums.append((search_artist, search_album)) else: partial_manual_items += 1 partial_description = 'Partial Match (Manual)' manual_albums.append((search_artist, search_album, artist, album, album_id)) print_partial(partial_description, ratio, artist, album, search_artist, search_album) else: exact_items += 1 print("{0: <30}: Artist: {1}, Album: {2}".format('Exact Match', artist, album)) matched_albums.append((artist, album, album_id)) else: no_items += 1 print("{0: <30}: Artist: {1}, Album: {2}".format('No Match', search_artist, search_album)) unmatched_albums.append((search_artist, search_album)) print_summary(total_items, partial_accepted_items, partial_rejected_items, partial_manual_items, exact_items, no_items) if (confirmation_dialog("Ok to proceed? (y/n)")): (manual_matched, manual_unmatched) = process_manual_albums(manual_albums) matched_albums += manual_matched unmatched_albums += manual_unmatched add_matched_albums_to_library(mob, matched_albums) upload_unmatched_albums_to_library(mmw, path, unmatched_albums)
print "found " + str(len(thumbsDownSongs)) + " dupe songs" #tracks = playlist['tracks'] #find_and_remove_dups(api, tracks) # Setup the gmusicapi api = Mobileclient() api.__init__() # Check to see if OAuth credentials available, ask user to setup if not try: api.oauth_login(Mobileclient.FROM_MAC_ADDRESS) #api.perform_oauth() except: print "No OAuth credentials found! Please setup in the following screen!" api.perform_oauth() api.oauth_login(Mobileclient.FROM_MAC_ADDRESS ) # If it fails here, it wasn't meant to be # Then, move on to doing all the work if api.is_authenticated(): print "Successfully logged in. Finding duplicates in playlists" dedupeSongs() api.logout() else: print "Not logged in! Exiting..." exit(1) print "Script has finished successfully!"
class GoogleMusicLogin(): def __init__(self): import requests from requests.packages.urllib3.exceptions import InsecureRequestWarning from requests.packages.urllib3.exceptions import InsecurePlatformWarning from requests.packages.urllib3.exceptions import SNIMissingWarning requests.packages.urllib3.disable_warnings(InsecureRequestWarning) requests.packages.urllib3.disable_warnings(InsecurePlatformWarning) requests.packages.urllib3.disable_warnings(SNIMissingWarning) self.gmusicapi = Mobileclient(debug_logging=False, validate=False, verify_ssl=False) #self.gmusicapi._session_class.lang = xbmc.getLanguage(xbmc.ISO_639_1, True) #utils.log(repr(xbmc.getLanguage(xbmc.ISO_639_1, True))) def checkCookie(self): # Remove cookie data if it is older then 7 days if utils.addon.getSetting('cookie-time'): import time if time.time() - float(utils.addon.getSetting('cookie-time')) > 3600*24*7: self.clearCookie() def checkCredentials(self): if not utils.addon.getSetting('username'): utils.addon.openSettings() if utils.addon.getSetting('password') and utils.addon.getSetting('password') != '**encoded**': import base64 utils.addon.setSetting('encpassword',base64.b64encode(utils.addon.getSetting('password'))) utils.addon.setSetting('password','**encoded**') def getApi(self): return self.gmusicapi def getStreamUrl(self, song_id, session_token, wentry_id): # retrieve registered device device_id = self.getDevice() # retrieve stream quality from settings quality = { '0':'hi','1':'med','2':'low' } [utils.addon.getSetting('quality')] utils.log("getStreamUrl songid: %s device: %s quality: %s"%(song_id, device_id, quality)) return self.gmusicapi.get_stream_url(song_id, device_id, quality, session_token, wentry_id) def getDevice(self): return utils.addon.getSetting('device_id') def initDevice(self): device_id = self.getDevice() if not device_id: utils.log('Trying to fetch the device_id') self.login() try: devices = self.gmusicapi.get_registered_devices() if len(devices) == 10: utils.log("WARNING: 10 devices already registered!") utils.log('Devices: '+repr(devices)) for device in devices: if device["type"] in ("ANDROID","PHONE","IOS"): device_id = str(device["id"]) break except Exception as e: utils.log("ERROR: "+repr(e)) if device_id: if device_id.lower().startswith('0x'): device_id = device_id[2:] utils.addon.setSetting('device_id', device_id) utils.log('Found device_id: '+device_id) else: utils.log('No Android device found in account') #self.language = utils.addon.getLocalizedString #dialog = xbmcgui.Dialog() #dialog.ok("", self.language(30111)) def clearCookie(self): utils.addon.setSetting('logged_in-mobile', "") utils.addon.setSetting('authtoken-mobile', "") utils.addon.setSetting('device_id', "") utils.addon.setSetting('subscriber', "0") def logout(self): self.gmusicapi.logout() def login(self, nocache=False): if not utils.addon.getSetting('logged_in-mobile') or nocache: import base64 utils.log('Logging in') self.checkCredentials() username = utils.addon.getSetting('username') password = base64.b64decode(utils.addon.getSetting('encpassword')) try: self.gmusicapi.login(username, password, self.getDevice() ) except: pass if not self.gmusicapi.is_authenticated(): try: utils.log("Login in with device_id failed, trying with MAC") self.gmusicapi.login(username, password, Mobileclient.FROM_MAC_ADDRESS) except Exception as e: utils.log("ERROR: "+repr(e)) if not self.gmusicapi.is_authenticated(): utils.log("Login failed") utils.addon.setSetting('logged_in-mobile', "") self.language = utils.addon.getLocalizedString dialog = xbmcgui.Dialog() dialog.ok(self.language(30101), self.language(30102)) raise else: utils.log("Login succeeded. Device id: "+self.gmusicapi.android_id) utils.addon.setSetting('device_id', self.gmusicapi.android_id) utils.addon.setSetting('logged_in-mobile', "1") utils.addon.setSetting('authtoken-mobile', self.gmusicapi.session._authtoken) import time utils.addon.setSetting('cookie-time', str(time.time())) utils.addon.setSetting('subscriber','1' if self.gmusicapi.is_subscribed else '0') else: utils.log("Loading auth from cache") self.gmusicapi.session._authtoken = utils.addon.getSetting('authtoken-mobile') self.gmusicapi.session.is_authenticated = True
logHndFile.setFormatter(logFormatter) logger.addHandler(logHndFile) logHndScreen = logging.StreamHandler(stream=sys.stdout) logHndScreen.setLevel(logging.ERROR) if args.verbose: logHndScreen.setLevel(logging.DEBUG) logHndScreen.setFormatter(logFormatter) logger.addHandler(logHndScreen) logger.info("gmusicapi log: " + utils.log_filepath) gpm = Mobileclient() gpm.oauth_login(Mobileclient.FROM_MAC_ADDRESS) if not gpm.is_authenticated(): logger.error("Login failed") exit() # Connect to MySQL # Mark all playlist entries as unprocessed db = pymysql.connect( host=config.db["hostname"], user=config.db["username"], password=config.db["password"], db=config.db["database"], autocommit=True ) cursor = db.cursor()
print "USAGE:" print "./add_last_songs_to_top_of_playlist.py 'PLAYLIST NAME' NUMBER_SONGS" print print "example: ./add_last_songs_to_top_of_playlist.py 'Ultimate Everything' 5" print " will move the last 5 songs in 'Ultimate Everything' to the top of the playlist." exit(0) else: playlist_name = sys.argv[1] num_songs = sys.argv[2] # Setup the gmusicapi api = Mobileclient() api.__init__() # Check to see if OAuth credentials available, ask user to setup if not try: api.oauth_login(Mobileclient.FROM_MAC_ADDRESS) except: print "No OAuth credentials found! Please setup in the following screen!" api.perform_oauth() api.oauth_login(Mobileclient.FROM_MAC_ADDRESS) # If it fails here, it wasn't meant to be # Then, move on to doing all the work if api.is_authenticated(): print "Successfully logged in. Moving " + num_songs + " tracks to top of playlist" playlists = api.get_all_user_playlist_contents() tracks = get_playlist_tracks(playlist_name, playlists) move_songs_to_top(api, tracks, int(num_songs)) print "Script completed successfully!"