class GoogleMusicHelper(object):
    def __init__(self, email=None, password=None):
        self.google_music_client = Mobileclient()
        if email and password:
            self.login(email, password)

    def login(self, email, password):
        if self.google_music_client.login(email, password,
                                          Mobileclient.FROM_MAC_ADDRESS):
            return "Logged in to Google"
        return "Error logging in"

    def add_song_by_name_to_google_library(self, song="", artist=""):
        results = self.google_music_client.search(query=song + " " + artist,
                                                  max_results=1)
        if results:
            track = results["song_hits"][0]["track"]
            return self.google_music_client.add_store_tracks(
                track.get("storeId") or track.get("nid"))

    def list_playlists(self):
        return self.google_music_client.get_all_user_playlist_contents()

    def sync_playlists_with_library(self, password=None, username=None):
        if self.google_music_client.login(username, password,
                                          Mobileclient.FROM_MAC_ADDRESS):
            all_tracks = []
            for playlist in self.google_music_client.get_all_user_playlist_contents(
            ):
                for track in playlist["tracks"]:
                    all_tracks.append(track["track"])

            playlist_store_ids = [track["storeId"] for track in all_tracks]
            all_songs = self.google_music_client.get_all_songs(
                incremental=False)

            print all_songs[0]

            added_store_ids = []
            for song in all_songs:
                store_id = None
                if song.get("nid"):
                    store_id = song["nid"]
                elif song.get("storeId"):
                    store_id = song["storeId"]
                added_store_ids.append(store_id)

            new_store_ids = set(playlist_store_ids) - set(added_store_ids)
            new_tracks = [
                track for track in all_tracks
                if track["storeId"] not in added_store_ids
            ]
            for storeId in new_store_ids:
                for track in new_tracks:
                    if track["storeId"] == storeId:
                        break
                print track['title'] + " by " + track["artist"]
                print self.google_music_client.add_store_tracks(storeId)
Example #2
0
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 gm_get_current_pl_member(client: Mobileclient, playlist: str) -> (str, list):
    playlist = list(filter(
        lambda x: x['deleted'] == False and x['name'] == playlist,
        client.get_all_user_playlist_contents()
    ))[0]
    member_track_ids = set([item['trackId'] for item in playlist['tracks']])
    return playlist['id'], [item for item in client.get_all_songs() if item['id'] in member_track_ids]
Example #4
0
class GMusicClient:
    def __init__(self):
        """
        This connects to the google music server by requesting credentials.
        """
        self.api = Mobileclient()

        # username = input('Type your Google Play Music email below.\n--> ')
        self.username = os.getenv('GOOGLE_USERNAME')
        dir_path = os.path.dirname(os.path.realpath(__file__)) + '/.cache-gmusic-' + ''.join(filter(str.isalpha, self.username))
        # Check if already authenticated
        if(not os.path.isfile(dir_path)):
            self.api.perform_oauth(open_browser=True, storage_filepath=dir_path)

        # Attempt to log in; if fail, get new token.
        try:
            self.api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, oauth_credentials=dir_path)
        except:
            self.api.perform_oauth(open_browser=True, storage_filepath=dir_path)
            self.api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, oauth_credentials=dir_path)

        print('Connected to GMusic')

    def get_playlists(self):
        """
        Gets all the playlists in Google Play Music. Some may not actually
        have any music, but they will be processed anyways.
        """
        playlists_cache_path = os.path.dirname(os.path.realpath(__file__)) + '/.cache-playlists_cache-' + ''.join(filter(str.isalpha, self.username))
        if (os.path.isfile(playlists_cache_path)):
            with open(playlists_cache_path, 'rb') as playlists_cache_file:
                playlists = pickle.load(playlists_cache_file)
        else:
            print('Requesting Google playlists')
            playlistsG = self.api.get_all_user_playlist_contents()
            print('Received Google playlists, we have', len(playlistsG), 'playlists')
            playlists = Playlists(playlistsG)
            with open(playlists_cache_path, 'wb') as playlists_cache_file:
                pickle.dump(playlists, playlists_cache_file)

        return playlists

    def get_all_songs(self):
        """
        Gets the entire Google library for adding to the
        """
        lib_cache_path = os.path.dirname(os.path.realpath(__file__)) + '/.cache-lib_cache-' + ''.join(filter(str.isalpha, self.username))
        if (os.path.isfile(lib_cache_path)):
            with open(lib_cache_path, 'rb') as lib_cache_file:
                library = pickle.load(lib_cache_file)
        else:
            print('Requesting Google library')
            librarySongs = self.api.get_all_songs()
            print('Received Google library, we have', len(librarySongs), 'songs')
            library = MusicLibrary(librarySongs)
            with open(lib_cache_path, 'wb') as lib_cache_file:
                pickle.dump(library, lib_cache_file)

        return library
Example #5
0
class Plugin:
    name = 'gmusic'

    def __init__(self, username, password):
        self.client = Mobileclient()
        self.client.login(username, password, Mobileclient.FROM_MAC_ADDRESS)
        # self.webclient = Webclient()
        # self.webclient.login(username, password)

    def get_tracks(self, artist=None, album=None):
        """
        Fetches tracks from api.

        If no filter is defined, it will get user tracks
        """
        return TrackList(self.client.get_all_songs())

    def get_playlists(self):
        """
        Get playlists and radios
        """
        playlists = []
        for playlist in self.client.get_all_user_playlist_contents():
            tracks = TrackList([
                self.client.get_track_info(x['trackId'])
                for x in playlist['tracks']
            ])
            playlists.append(PlayList(playlist['name'], tracks))
        return playlists

    def stream(self, track):
        def _stream(url):
            inp = requests.get(url, stream=True)
            chunk_size = 1024
            for chunk in inp.iter_content(chunk_size):
                if not chunk:
                    continue
                yield chunk

        song_id = track.uri.split(':')[-1]
        return _stream(self.client.get_stream_url(song_id))

    def search(self, keywords, matches):
        results = self.client.search(keywords)
        if matches == 'artist':
            return {'artists': results.get('artist_hits', [])}
        elif matches == 'album':
            return {'albums': results.get('album_hits', [])}
        elif matches == 'tracks':
            return {'tracks': results.get('song_hits', [])}
        elif matches == 'all':
            return {
                'artists': results.get('artist_hits', []),
                'albums': results.get('album_hits', []),
                'tracks': results.get('song_hits', [])
            }
Example #6
0
class GMusicAPI():
    def __init__(self, username=None, encrypted_pass=None):
        self._api = Mobileclient()
        self.logged_in = False
        if username and encrypted_pass:
            self.login(username, encrypted_pass)

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

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

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

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

        result = self._api.search(query)

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

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

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

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

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

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

    def mapUnknownTracks(self, db):
        playlist = db.unmappedTracks()

        for track in playlist:
            searchstr = track.artist + " " + track.song
            print("Searching for %s" % (searchstr))
            try:
                result = self.api.search_all_access(searchstr, max_results=1)
                print("Found " + result['song_hits'][0]['track']['artist'] +
                      " - " + result['song_hits'][0]['track']['title'])
                nid = result['song_hits'][0]['track']['nid']
                db.storemapping(track.song, track.artist, nid)
            except:
                print("Error parsing result: " + str(result))

            time.sleep(1)

    def maintain(self, tracks):
        print("Searching for playlist %s" % (self.playlistName))

        found = False
        searchres = self.api.get_all_playlists()
        for list in searchres:
            if list['name'] == self.playlistName:
                found = True
                pid = list['id']

        if not found:
            print("Not found - creating")
            pid = self.api.create_playlist(self.playlistName)

        print("Playlist id is %s" % (pid))

        print("Getting current contents")
        playlists = self.api.get_all_user_playlist_contents()
        currentEntries = []
        for playlist in playlists:
            if playlist['name'] == self.playlistName:
                for entry in playlist['tracks']:
                    currentEntries.append(entry['id'])

        print("Removing songs")
        self.api.remove_entries_from_playlist(currentEntries)

        print("Adding songs")
        self.api.add_songs_to_playlist(pid, tracks)
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
Example #9
0
class GMusicWS:
	def __init__(self, user, password, playlistName):
		self.playlistName = playlistName
		self.api = Mobileclient()
		print ("Logging into MobileClient API")
		self.api.login(user, password,"android_id") #insert unique android_id here

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

		print ("Removing songs")		
		self.api.remove_entries_from_playlist(currentEntries)
		
		print ("Adding songs")
		self.api.add_songs_to_playlist(pid, tracks)
def get_online_library():
    api = Mobileclient()
    logged_in = api.login("*****@*****.**", "yesilly12twat",
                          Mobileclient.FROM_MAC_ADDRESS)
    if logged_in == False:
        sys.exit("Couldn't log in.")
    if not api.is_authenticated:
        sys.exit("Couldn't log in. Wrong credentials")

    library = {
        "tracks": api.get_all_songs(),
        "playlists": api.get_all_user_playlist_contents(),
    }
    return library
Example #11
0
class Plugin:
    name = 'gmusic'

    def __init__(self, username, password):
        self.client = Mobileclient()
        self.client.login(username, password, Mobileclient.FROM_MAC_ADDRESS)
        # self.webclient = Webclient()
        # self.webclient.login(username, password)

    def get_tracks(self, artist=None, album=None):
        """
        Fetches tracks from api.

        If no filter is defined, it will get user tracks
        """
        return TrackList(self.client.get_all_songs())

    def get_playlists(self):
        """
        Get playlists and radios
        """
        playlists = []
        for playlist in self.client.get_all_user_playlist_contents():
            tracks = TrackList([self.client.get_track_info(x['trackId']) for x in playlist['tracks']])
            playlists.append(PlayList(playlist['name'], tracks))
        return playlists

    def stream(self, track):
        def _stream(url):
            inp = requests.get(url, stream=True)
            chunk_size = 1024
            for chunk in inp.iter_content(chunk_size):
                if not chunk:
                    continue
                yield chunk
        song_id = track.uri.split(':')[-1]
        return _stream(self.client.get_stream_url(song_id))

    def search(self, keywords, matches):
        results = self.client.search(keywords)
        if matches == 'artist':
            return {'artists': results.get('artist_hits', [])}
        elif matches == 'album':
            return {'albums': results.get('album_hits', [])}
        elif matches == 'tracks':
            return {'tracks': results.get('song_hits', [])}
        elif matches == 'all':
            return {'artists': results.get('artist_hits', []),
                    'albums': results.get('album_hits', []),
                    'tracks': results.get('song_hits', [])}
def main():
    parser = argparse.ArgumentParser(description='Sync iTunes Playlists to Google Play Music.')
    parser.add_argument('itunes_music_library', type=str,
                        help='Path to iTunes Music Library.xml')
    parser.add_argument('google_music_manager_db', type=str,
                        help='Path to Google Music Manager ServerDatabase.db')
    parser.add_argument('--verbose', action='store_true', default=False,
                        help='Print verbose output')
    parser.add_argument('playlists', type=str, nargs='*',
                        metavar='playlist',
                        help='Names of playlists to sync')
    args = parser.parse_args()
    global verbose
    verbose = args.verbose
    lib = pyItunes.Library(args.itunes_music_library)
    known_itunes_playlists = lib.getPlaylistNames()
    if args.playlists:
        itunes_playlists = args.playlists
        not_found = set(itunes_playlists) - set(known_itunes_playlists)
        if not_found:
            print('''Error: these playlists aren't in your iTunes Library:
%s
''' % (sorted(not_found), ))
            return 1
    else:
        itunes_playlists = known_itunes_playlists

    server_db = sqlite3.connect(args.google_music_manager_db)
    api = None
    username, password = open(os.path.join(os.path.dirname(__file__),
                                           'auth.txt'),
                              'r').read().splitlines()
    try:
        api = Mobileclient()
        if not api.login(username, password):
            print('Error: unable to login', file=sys.stderr)
            return 1
        all_google_playlists = api.get_all_user_playlist_contents()
        google_playlists = {p['name']: p for p in all_google_playlists}
        for name in itunes_playlists:
            sync_playlist(api, server_db, lib.getPlaylist(name),
                          google_playlists)
    finally:
        if api:
            api.logout()
    return 0
Example #13
0
class GoogleMusicClient(object):

    def __init__(self, email, password, device_id):
        self.api = Mobileclient()
        self.api.login(email, password)
        self.device_id = device_id

        self.playlists = []
        self.songs = {}

        self.player = Player()

    def update_library(self):
        self.songs = {song.get('nid'): song for song in self.api.get_all_songs()}

        self.playlists = [
            Playlist(name=playlist['name'].encode('utf-8'),
                     tracks=[track['trackId'] for track in playlist['tracks']])
            for playlist in self.api.get_all_user_playlist_contents()
        ]

    def play(self, item_type, item_index):
        if item_type == 'playlist':
            click.echo('playing {} {}'.format(item_type, self.playlists[int(item_index)].name))

    def queue(self, item_type, item_index):
        if item_type == 'playlist':
            for song in self.playlists[int(item_index)]:
                self.player.enqueue(song)

    def show(self, item_type, item_index):
        if item_type == 'playlist':
            playlist = self.playlists[int(item_index)]
            click.echo('showing {} {}'.format(item_type, playlist.name))
            for song in playlist.tracks:
                click.echo(self.songs[song])
        elif item_type == 'queue':
            for song in self.player.queue:
                click.echo(song)

    def list(self, item_type):
        if item_type == 'playlist':
            for i, playlist in enumerate(self.playlists):
                click.echo("[{}]\t{} ({})".format(i, playlist.name, len(playlist.tracks)))
        elif item_type == 'songs':
            click.echo(self.songs)
def get_albums_from_playlist(config):
    login, password, playlist_name, android_id = map(config.get, ('login', 'password', 'playlist', 'android_id'))
    api = Mobileclient()
    if not android_id:
        android_id = Mobileclient.FROM_MAC_ADDRESS 
    try:
        api.login(login, password, android_id)
        all_playlists = api.get_all_user_playlist_contents()
        matched_playlist = next(playlist for playlist in all_playlists if playlist['name'].lower() == playlist_name.lower())
        album_list = {(entry['track']['albumArtist'], entry['track']['album'])
              for entry in matched_playlist['tracks'] if 'track' in entry}
        return album_list
    except StopIteration:
        sys.exit('playlist not found.')
    except NotLoggedIn:
        sys.exit('wrong username or password.')
    finally:
        api.logout()
Example #15
0
    def analyse(self, device_id, artists_number):
        """The main method that analysis Google Play Music account,
        and returns results of parsing concert.ua

        :param device_id: str
        :param artists_number: int
        :return: results: list
        """

        api = Mobileclient()
        try:
            artists_number = int(artists_number)
            device_id = str(device_id)
        except ValueError:
            return -1
        api.oauth_login(str(device_id))

        main_playlist = Playlist()

        library = api.get_all_songs()
        for song in library:
            main_playlist.append(song)

        playlists = api.get_all_user_playlist_contents()
        for playlist in playlists:
            for song in playlist['tracks']:
                main_playlist.append(song)

        top = main_playlist.top_n(artists_number)
        artists = []
        for artist_id in top:
            artist = api.get_artist_info(include_albums=False,
                                         artist_id=artist_id,
                                         max_rel_artist=0,
                                         max_top_tracks=0)
            artists.append(artist['name'])

        results = []
        for artist in artists:
            parse_result = Parser().parse(artist)
            if parse_result:
                results.append(parse_result)

        return results
Example #16
0
class GmSession:
    def __init__(self):
        self.session = Mobileclient()
        self.device_id = gmusic_device_id
        self.cred_path = gmusic_cred_path
        self.playlist_id = gmusic_playlist_id

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

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

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

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

                print('Adding new songs...')
                res = self.session.add_songs_to_playlist(
                    self.playlist_id, song_list)
                print('Removing previous songs...')
                out = self.session.remove_entries_from_playlist(to_remove)
                print('Finished')
Example #17
0
File: app.py Project: kyeah/gspot
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
Example #18
0
class GMusicClient:
    def __init__(self):
        """
        This connects to the google music server by requesting credentials.
        """
        self.api = Mobileclient()

        username = input('Type your Google Play Music email below.\n--> ')
        dir_path = os.path.dirname(
            os.path.realpath(__file__)) + '/.cache-gmusic-' + ''.join(
                filter(str.isalpha, username))
        # Check if already authenticated
        if (not os.path.isfile(dir_path)):
            self.api.perform_oauth(open_browser=True,
                                   storage_filepath=dir_path)

        # Attempt to log in; if fail, get new token.
        try:
            self.api.oauth_login(Mobileclient.FROM_MAC_ADDRESS)
        except:
            self.api.perform_oauth(open_browser=True,
                                   storage_filepath=dir_path)
            self.api.oauth_login(Mobileclient.FROM_MAC_ADDRESS)

        print('Connected to GMusic')

    def get_playlists(self):
        """
        Gets all the playlists in Google Play Music. Some may not actually
        have any music, but they will be processed anyways.
        """
        return Playlists(self.api.get_all_user_playlist_contents())

    def get_all(self):
        """
        Gets the entire Google library for adding to the
        """
        print('Requesting Google library')
        library = self.api.get_all_songs()
        print('Received Google library, we have', len(library), 'songs')
        return MusicLibrary(library)
Example #19
0
class Gopma():
    def __init__(self, action=None):
        print "Initialising GOPMA."
        config = ConfigParser.ConfigParser()
        config.read('config.ini')

        email = config.get('login', 'email')
        password = config.get('login', 'password')
        try:
            auth_token = config.get('login', 'auth_token')
        except:
            auth_token = False
            print "No auth token could be found"

        print "Logging into Google Play Music as", email
        logged_in = False
        bad_auth = False
        while not logged_in:
            if not auth_token or bad_auth:
                self.api = Mobileclient()
                login = self.api.login(email, password,
                                       Mobileclient.FROM_MAC_ADDRESS)
                if not login:
                    print "Login failed, check your credentials."
                    sys.exit()

                # Save the auth token for later
                with open('config.ini', 'w+') as f:
                    config.set('login', 'auth_token',
                               self.api.session._authtoken)
                    config.write(f)
                    f.close()
                    print "Saved auth token for later."

                logged_in = True
            else:
                print "Found an auth token, trying it."
                self.api = Mobileclient()
                self.api.session._authtoken = auth_token
                self.api.session.is_authenticated = True
                try:
                    # Test the auth token
                    self.api.get_registered_devices()
                    logged_in = True
                except:
                    # Failed
                    print "Bad auth token, manually signing in."
                    bad_auth = True
        print "Successfully logged in as", email

        if action != 'reset_genres':
            print "Loading data."
            self.playlists = self.api.get_all_playlists()
            self.content = self.api.get_all_user_playlist_contents()
            self.root_genres, self.child_genres = self.load_genres()
            print "Data successfully loaded."

    def create_or_retrieve_playlists(self, playlists):
        """ Helper function to create or retrieve playlist IDs for a given agg_lists

            Input: List of playlist names
            Output: Dict of playlist names and IDs
        """
        if type(playlists) is not list:
            print "Stop passing non-lists to this function."
            sys.exit()

        agg_lists = [
            p for p in self.content
            if p.get('type') == 'USER_GENERATED' and p.get('name') in playlists
        ]

        # Get all playlist IDs
        agg_playlists = {}
        existing_playlists = [playlist['name'] for playlist in agg_lists]
        for name in playlists:
            if name not in existing_playlists:
                print "Playlist not found, creating", name
                agg_playlists[name] = self.api.create_playlist(name)
                self.api.edit_playlist(agg_playlists[name], public=True)
            else:
                print "Playlist found", name + ", retrieving ID."
                playlist_id = [
                    p['id'] for p in agg_lists if p.get('name') == name
                ][0]
                agg_playlists[name] = playlist_id
                # self.api.edit_playlist(agg_playlists[name], public=True)

        return agg_playlists

    def load_genres(self, reset=False):
        """ Load all genres
        """
        # Get the root genres
        if os.path.isfile(ROOT_GENRE_FILE):
            print "Found a root genres file."
            if reset:
                root_genres = self.api.get_genres()

                with open(ROOT_GENRE_FILE, 'w') as fp:
                    pickle.dump(root_genres, fp)
                print "Root genres have been reset."
            else:
                with open(ROOT_GENRE_FILE) as fp:
                    root_genres = pickle.load(fp)
        else:
            print "Couldn't find a root genres file, retrieving data."
            root_genres = self.api.get_genres()

            with open(ROOT_GENRE_FILE, 'w') as fp:
                pickle.dump(root_genres, fp)

            print "Root genres file created."

        # Get the child genres
        if os.path.isfile(CHILD_GENRE_FILE):
            print "Found a child genres file."
            if reset:
                child_genres = {}

                for genre in root_genres:
                    children = self.api.get_genres(genre['id'])
                    child_names = []
                    for child in children:
                        child_names.append(child['name'])
                    child_genres[genre['id']] = child_names

                with open(CHILD_GENRE_FILE, 'w') as fp:
                    pickle.dump(child_genres, fp)
                print "Child genres have been reset."
            else:
                with open(CHILD_GENRE_FILE) as fp:
                    child_genres = pickle.load(fp)
        else:
            print "Couldn't find a child genres file, retrieving data."
            child_genres = {}

            for genre in root_genres:
                children = self.api.get_genres(genre['id'])
                child_names = []
                for child in children:
                    child_names.append(child['name'])
                child_genres[genre['id']] = child_names

            with open(CHILD_GENRE_FILE, 'w') as fp:
                pickle.dump(child_genres, fp)
            print "Child genres file created."

        return root_genres, child_genres

    def delete_empty_playlists(self):
        """ Delete ALL empty playlists. Be careful with this.
        """
        playlists = self.content
        for playlist in playlists:
            if len(playlist['tracks']
                   ) == 0 and playlist['name'] != AGGREGATE_PLAYLIST_NAME:
                self.api.delete_playlist(playlist['id'])
                print "Deleted", playlist['name']

    def create_playlists(self):
        """ Create all needed playlists
        """
        print "Creating/updating playlists."
        self.create_or_retrieve_playlists(
            [AGGREGATE_PLAYLIST_NAME, SHARED_PLAYLIST_NAME])
        self.create_or_retrieve_playlists(
            [PLAYLIST_PREFIX + genre for genre in GENRE_PLAYLISTS.values()])

    def get_playlist_urls(self):
        """ Get all gopma playlist URLS
        """
        urls = {}
        for playlist in self.playlists:
            if PLAYLIST_PREFIX in playlist['name'] and playlist[
                    'type'] == 'USER_GENERATED':
                urls[playlist[
                    'name']] = "https://play.google.com/music/playlist/" + playlist[
                        'shareToken']
        return urls

    def get_playlist_id(self, name):
        """ Get the playlist ID for a given playlist name
        """
        playlist = [p for p in self.playlists if p.get('name') == name][0]
        return playlist['id']

    def get_share_token(self, playlist_id):
        """ Get the share token for a given playlist ID
        """
        playlist = [p for p in self.playlists if p.get('id') == playlist_id]
        return playlist[0]['shareToken']

    def get_playlist_tracks(self, playlist_id):
        """ Get the tracks for a specified playlist id
        """
        return [p for p in self.content
                if p.get('id') == playlist_id][0]['tracks']

    def get_parent_genre_id(self, genre_name):
        """ Get the parent id for a given genre name
        """
        # Check the root genres first
        for genre in self.root_genres:
            if genre_name == genre['name']:
                return genre['id']

        # Check children genres
        for gid, genres in self.child_genres.items():
            for genre in genres:
                if genre == genre_name:
                    return gid

    def wipe_all_playlists(self):
        """ Wipe all Gopma playlists
        """
        for playlist in self.playlists:
            if PLAYLIST_PREFIX in playlist[
                    'name'] and SHARED_PLAYLIST_NAME not in playlist['name']:
                print "Wiping playlist: ", playlist['name']
                self.wipe_playlist(playlist['id'])

    def wipe_playlist(self, playlist_id):
        """ Wipe a given playlist
        """
        playlist_tracks = self.get_playlist_tracks(playlist_id)
        song_ids = [track['id'] for track in playlist_tracks]
        self.api.remove_entries_from_playlist(song_ids)

    def reset_daily_playlists(self):
        """ Reset the daily playlists
        """
        # Get playlists
        agg_playlists = self.create_or_retrieve_playlists([TODAY, YESTERDAY])
        yest_id = agg_playlists[YESTERDAY]
        today_id = agg_playlists[TODAY]

        # Wipe yesterday
        print "Wiping yesterday's playlist."
        self.wipe_playlist(yest_id)

        # Copy today to yesterday
        print "Copying", TODAY, "to", YESTERDAY
        today_tracks = self.get_playlist_tracks(today_id)
        self.api.add_songs_to_playlist(yest_id,
                                       [t['trackId'] for t in today_tracks])

        # Wipe today
        print "Wiping today's playlist."
        self.wipe_playlist(today_id)

    def update_group_playlist(self):
        """ Update the big group aggregate and the daily playlist with any new shared songs
        """
        # Get the aggregate playlist songs
        agg_token = self.get_share_token(
            self.get_playlist_id(AGGREGATE_PLAYLIST_NAME))
        agg_playlists = [
            p for p in self.playlists if p.get('type') == 'USER_GENERATED'
            and p.get('shareToken') == agg_token
        ]
        agg_id = agg_playlists[0]['id']

        # Get tracks
        agg_tracks = self.api.get_shared_playlist_contents(agg_token)
        agg_tracks_ids = [track['trackId'] for track in agg_tracks]
        print "Updating group playlists."

        # Get the playlists we want to update with
        shared_lists = [
            p for p in self.playlists if p.get('name') == SHARED_PLAYLIST_NAME
        ]

        for playlist in shared_lists:
            shared_tracks = self.api.get_shared_playlist_contents(
                playlist['shareToken'])
            print "\nRetrieving from", playlist[
                'name'], "by", playlist['ownerName'] + ":"

            # Add songs to aggregate playlist
            if len(shared_tracks) == 0:
                print "<< Playlist is empty. >>"
            else:
                no_new = True
                for track in shared_tracks:
                    if track['trackId'] not in agg_tracks_ids:
                        # Add to giant aggregate playlist
                        self.api.add_songs_to_playlist(agg_id,
                                                       track['trackId'])
                        # Add to daily playlist
                        self.api.add_songs_to_playlist(
                            self.get_playlist_id(TODAY), track['trackId'])
                        # Add to genre relevant playlist
                        self.api.add_songs_to_playlist(
                            self.get_playlist_id(
                                PLAYLIST_PREFIX +
                                GENRE_PLAYLISTS[self.get_parent_genre_id(
                                    track['track']['genre'])]),
                            track['trackId'])
                        title = track['track']['title'].encode(
                            'ascii', 'ignore')
                        artist = track['track']['artist'].encode(
                            'ascii', 'ignore')
                        print "+", title, "by", artist, "has been added."
                        no_new = False
                if no_new:
                    print "<< There are no new tracks to be added from this playlist. >>"

        print "Finished updating group playlists."

    def update_songs(self):
        """ Update the database with song information
        """
        # Connect to the database
        config = ConfigParser.ConfigParser()
        config.read('config.ini')

        dbname = config.get('database', 'dbname')
        dbuser = config.get('database', 'user')
        dbhost = config.get('database', 'host')
        dbpass = config.get('database', 'password')

        try:
            conn = psycopg2.connect("dbname=" + dbname + " user="******" host=" + dbhost + " password="******"<< Could not connect to the db. >>"
            print e
            sys.exit()

        # Update songs
        for c in self.content:
            if c.get('type') == 'USER_GENERATED' and c.get(
                    'name') == AGGREGATE_PLAYLIST_NAME:
                songs = c.get('tracks')
                for s in songs:
                    # Song details
                    details = s['track']
                    # Date song was added
                    date_added = datetime.fromtimestamp(
                        int(s.get('creationTimestamp')) / 1000000)
                    # Values to save
                    values = [
                        str(s.get('trackId')),
                        str(details.get('title').encode('UTF-8', 'ignore')),
                        str(details.get('artist').encode('UTF-8', 'ignore')),
                        str(details.get('album').encode('UTF-8', 'ignore')),
                        str(details.get('genre')), date_added
                    ]
                    # SQL query
                    insert_song = "INSERT INTO playlists_song VALUES (%s, %s, %s, %s, %s, %s) ON CONFLICT (tid) DO NOTHING;"
                    # Save to DB
                    self.commit_changes(conn, cur, insert_song, values)

        print "Songs successfully updated."
        # Close connection
        cur.close()
        conn.close()

    def commit_changes(self, conn, cur, query, values):
        try:  # Commit our changes made
            cur.execute(query, values)
            conn.commit()
        except psycopg2.Error as exc:
            print exc
            sys.exit()
Example #20
0
class Player(object):
    def __init__(self, device_id):
        self.api = Mobileclient()
        self.api.logger.setLevel(logging.INFO)
        #print(utils.log_filepath)

        options = ["--aout=alsa", "-I dummy", "--fullscreen"]
        self.vlc = Instance(options)
        self.player = None

        self.loaded_tracks = []

        self.playing = False
        self.repeat = Repeat.none
        self.random = False
        self.song_index = 0
        self.now_playing_title = ""
        self.now_playing_artist = ""
        self.now_playing_playlist = ""

        # 取得したjsonの生データ
        self.song_library = []
        self.playlist_library = []
        # 整頓した楽曲ライブラリ
        self.songs = []
        self.albums = []
        self.playlists = []
        self.artists = []

        # play musicログイン
        if not os.path.exists(CREDENTIAL_FILE):
            self.api.perform_oauth(CREDENTIAL_FILE)
        self.api.oauth_login(device_id, CREDENTIAL_FILE)
        # 曲一覧読み込み
        if os.path.isfile(JSON_DIR + "songs.json"):
            # Load from file
            print("Found songs data.")
            with open(JSON_DIR + 'songs.json') as input_file:
                self.song_library = json.load(input_file)
        else:
            self.song_library = self.api.get_all_songs()
            # Save to file
            with open(JSON_DIR + 'songs.json', 'w') as output_file:
                json.dump(self.song_library, output_file)

        self.create_songs()
        self.create_albums()
        self.create_artists()

        # プレイリスト読み込み
        if os.path.isfile(JSON_DIR + "playlists.json"):
            # Load from file
            print("Found playlist data.")
            with open(JSON_DIR + 'playlists.json') as input_file:
                self.playlist_library = json.load(input_file)
        else:
            self.playlist_library = self.api.get_all_user_playlist_contents()
            # Save to file
            with open(JSON_DIR + 'playlists.json', 'w') as output_file:
                json.dump(self.playlist_library, output_file)
        #プレイリスト名編集
        self.create_playlists()

        # 定時ライブラリ更新処理
        t = threading.Timer(RELOAD_LIB_TIME, self.auto_reload)
        t.start()

    def auto_reload(self):
        while True:
            if not self.playing:
                break
            time.sleep(60)
        self.reload_library()
        print("[ music list auto reloaded ]")
        t = threading.Timer(RELOAD_LIB_TIME, self.auto_reload)
        t.start()

    def reload_library(self):
        # 曲一覧読み込み
        self.song_library = self.api.get_all_songs()
        # Save to file
        with open(JSON_DIR + 'songs.json', 'w') as output_file:
            json.dump(self.song_library, output_file)

        self.create_songs()
        self.create_albums()
        self.create_artists()
        # プレイリスト読み込み
        self.playlist_library = self.api.get_all_user_playlist_contents()
        # Save to file
        with open(JSON_DIR + 'playlists.json', 'w') as output_file:
            json.dump(self.playlist_library, output_file)
        #プレイリスト名編集
        self.create_playlists()

    def create_songs(self):
        self.songs = []
        # 曲名編集
        for index, song in enumerate(self.song_library):
            self.songs.append({})
            self.songs[index].update({"original_name": song['title']})
            self.songs[index].update(
                {"name": cir.convert_into_romaji(song['title'])})
            self.songs[index].update({"artist": song['artist']})
            self.songs[index].update({"trackId": song['id']})
            self.songs[index].update({"source": 1})
            #print(self.songs[index])
            #sleep(0.1)
        print("[ create_songs finished ]")

    def create_playlists(self):
        self.playlists = []
        #プレイリスト名編集
        for index, playlist in enumerate(self.playlist_library):
            self.playlists.append({})
            self.playlists[index].update({"original_name": playlist['name']})
            self.playlists[index].update(
                {"name": cir.convert_into_romaji(playlist['name'])})
            self.playlists[index].update({"tracks": playlist['tracks']})
            print(self.playlists[index]['name'])
        print("[ create_playlists finished ]")

    def create_albums(self):
        self.albums = []
        # アルバムリスト作成
        for song in self.song_library:
            album_found = False
            track = {}
            for index, album in enumerate(self.albums):
                # アルバムがすでに登録されていた場合
                if album['original_name'] == song['album']:
                    album_found = True
                    track.update({"trackId": song['id']})
                    track.update({"source": 1})
                    track.update({"trackNumber": song['trackNumber']})
                    self.albums[index]['tracks'].append(track)
                    #print(self.albums[index])
                    break
            if album_found:
                continue

            #新規アルバム作成
            albums_len = len(self.albums)
            self.albums.append({})
            self.albums[albums_len].update({"original_name": song['album']})
            self.albums[albums_len].update(
                {"name": cir.convert_into_romaji(song['album'])})

            track.update({"trackId": song['id']})
            track.update({"source": 1})
            track.update({"trackNumber": song['trackNumber']})
            self.albums[albums_len].update({"tracks": [track]})
            #print(self.albums[albums_len])
        # tracknumberでソート
        for album in self.albums:
            album['tracks'] = sorted(album['tracks'],
                                     key=lambda x: x['trackNumber'])
            print(album["name"])

        print("[ create_albums finished ]")

    def create_artists(self):
        self.artists = []
        # アーティストリスト作成
        for song in self.song_library:
            artist_found = False
            track = {}
            for index, artist in enumerate(self.artists):
                # アーティストがすでに登録されていた場合
                if artist['original_name'] == song['artist']:
                    artist_found = True
                    track.update({"trackId": song['id']})
                    track.update({"source": 1})
                    track.update({"trackNumber": song['trackNumber']})
                    self.artists[index]['tracks'].append(track)
                    break
            if artist_found:
                continue

            #新規アルバム作成
            artists_len = len(self.artists)
            self.artists.append({})
            self.artists[artists_len].update({"original_name": song['artist']})
            self.artists[artists_len].update(
                {"name": cir.convert_into_romaji(song['artist'])})

            track.update({"trackId": song['id']})
            track.update({"source": 1})
            track.update({"trackNumber": song['trackNumber']})
            self.artists[artists_len].update({"tracks": [track]})
            print(self.artists[artists_len]["name"])
        print("[ create_artists finished ]")

    def load_playlist(self, name):
        name = name.strip().lower()
        print("Looking for...", name)

        top_diff = 0.0
        top_playlist = {}
        # 検索
        for playlist_dict in self.playlists:
            playlist_name = playlist_dict['name'].strip().lower()
            diff = difflib.SequenceMatcher(None, playlist_name, name).ratio()

            if diff > top_diff:
                print("diff match...", playlist_dict['name'], ":", diff)
                top_playlist = playlist_dict
                top_diff = diff
            else:
                pass
                #print("Found...", playlist_dict['name'])
        # 一番マッチしたものを返す
        if top_diff > DIFF_ARGS:
            self.loaded_tracks = []
            print(top_diff)
            print("Found match...", top_playlist['name'])
            for track_dict in top_playlist['tracks']:
                self.loaded_tracks.append(track_dict)
            self.now_playing_playlist = top_playlist['original_name']
            return top_playlist['original_name']
        else:
            return None

    def load_song(self, name):
        name = name.strip().lower()
        print("Looking for...", name)

        top_diff = 0.0
        top_song = {}
        for song_dict in self.songs:
            song_name = song_dict['name'].strip().lower()
            diff = difflib.SequenceMatcher(None, song_name, name).ratio()
            #print(diff)
            if diff > top_diff:
                print("diff match...", song_dict['name'], ":", diff)
                top_song = song_dict
                top_diff = diff
            else:
                pass
                #print("Found...", song_dict['name'])
        # 一番マッチしたものを返す
        if top_diff > DIFF_ARGS:
            self.loaded_tracks = []
            print(top_diff)
            print("Found match...", top_song['name'])
            self.loaded_tracks.append(top_song)
            self.now_playing_playlist = ""
            return top_song['original_name']
        else:
            return None

    def load_album(self, name):
        name = name.strip().lower()
        print("Looking for...", name)

        top_diff = 0.0
        top_album = {}
        for album_dict in self.albums:
            album_name = album_dict['name'].strip().lower()
            diff = difflib.SequenceMatcher(None, album_name, name).ratio()
            #print(diff)
            if diff > top_diff:
                print("diff match...", album_dict['name'], ":", diff)
                top_album = album_dict
                top_diff = diff
            else:
                pass
                #print("Found...", album_dict['name'])
        # 一番マッチしたものを返す
        if top_diff > DIFF_ARGS:
            self.loaded_tracks = []
            print(top_diff)
            print("Found match...", top_album['name'])
            for track_dict in top_album['tracks']:
                self.loaded_tracks.append(track_dict)
            self.now_playing_playlist = top_album['original_name']
            return top_album['original_name']
        else:
            return None

    def load_artist(self, name):
        name = name.strip().lower()
        print("Looking for...", name)

        top_diff = 0.0
        top_artist = {}
        for artist_dict in self.artists:
            artist_name = artist_dict['name'].strip().lower()
            diff = difflib.SequenceMatcher(None, artist_name, name).ratio()
            #print(diff)
            if diff > top_diff:
                print("diff match...", artist_dict['name'], ":", diff)
                top_artist = artist_dict
                top_diff = diff
            else:
                pass
        # 一番マッチしたものを返す
        if top_diff > DIFF_ARGS:
            self.loaded_tracks = []
            print(top_diff)
            print("Found match...", top_artist['name'])
            for track_dict in top_artist['tracks']:
                self.loaded_tracks.append(track_dict)
            self.now_playing_playlist = top_artist['original_name']
            return top_artist['original_name']
        else:
            return None

    def load_cloud(self, name, isArtist=True, isSong=True, isAlbum=True):
        search = self.api.search(name)

        # アーティストのトップ曲を流す
        if search["artist_hits"] and isArtist:
            for index in range(len(search["artist_hits"])):
                artist_id = search["artist_hits"][index]["artist"]["artistId"]
                artist = self.api.get_artist_info(artist_id,
                                                  max_top_tracks=MAX_TRACK,
                                                  include_albums=False,
                                                  max_rel_artist=0)
                if "topTracks" in artist.keys():
                    break
            if "topTracks" in artist.keys():
                self.loaded_tracks = []
                for track_dict in artist["topTracks"]:
                    track_dict.update({"track": track_dict})
                    track_dict.update({"trackId": track_dict["storeId"]})
                    track_dict.update({"source": "2"})
                    self.loaded_tracks.append(track_dict)
                self.now_playing_playlist = ""
                return artist["name"]

        # 単曲を流す(複数にしたほうがいいかも)
        elif search["song_hits"] and isSong:
            self.loaded_tracks = []
            for index, track_dict in enumerate(search["song_hits"]):
                if index >= MAX_TRACK: break
                track_dict.update({"trackId": track_dict["track"]["storeId"]})
                track_dict.update({"source": "2"})
                self.loaded_tracks.append(track_dict)
            self.now_playing_playlist = ""
            return self.loaded_tracks[0]["track"]["title"]

        # アルバムを流す(正確さに欠ける)
        elif search["album_hits"] and isAlbum:
            album_id = search["album_hits"][0]["album"]["albumId"]
            album = self.api.get_album_info(album_id)
            self.loaded_tracks = []
            for track_dict in album["tracks"]:
                track_dict.update({"track": track_dict})
                track_dict.update({"trackId": track_dict["storeId"]})
                track_dict.update({"source": "2"})
                self.loaded_tracks.append(track_dict)
            self.now_playing_playlist = album["name"]
            return album["name"]

        # ステーション(ここまで回ってこない気が・・・)
        elif search["station_hits"]:
            pass
        return None

    def end_callback(self, event, track_index):
        # ランダム再生時処理
        if self.random:
            self.song_index = random.randint(0, len(self.loaded_tracks) - 1)
            self.play_song(self.loaded_tracks[self.song_index])
            event_manager = self.player.event_manager()
            event_manager.event_attach(EventType.MediaPlayerEndReached,
                                       self.end_callback, self.song_index + 1)
            return
        # 一曲リピート
        if self.repeat == Repeat.song:
            self.play_song(self.loaded_tracks[track_index - 1])
            event_manager = self.player.event_manager()
            event_manager.event_attach(EventType.MediaPlayerEndReached,
                                       self.end_callback, track_index)
            return
        # 通常再生・プレイリストリピート
        if track_index < len(self.loaded_tracks):
            self.song_index = track_index
            self.play_song(self.loaded_tracks[track_index])
            event_manager = self.player.event_manager()
            event_manager.event_attach(EventType.MediaPlayerEndReached,
                                       self.end_callback, track_index + 1)
        else:
            if self.repeat == Repeat.playlist:
                self.start_playlist()
            else:
                self.playing = False
                self.song_index = 0

    def start_playlist(self):
        if len(self.loaded_tracks) > 0:
            # ランダム再生時処理
            if self.random:
                self.song_index = random.randint(0,
                                                 len(self.loaded_tracks) - 1)
            else:
                # 通常再生
                self.song_index = 0
            self.play_song(self.loaded_tracks[self.song_index])
            event_manager = self.player.event_manager()
            event_manager.event_attach(EventType.MediaPlayerEndReached,
                                       self.end_callback, self.song_index + 1)
            return True
        return False

    def play_song(self, song_dict):
        stream_url = self.api.get_stream_url(song_dict['trackId'])
        self.player = self.vlc.media_player_new()
        media = self.vlc.media_new(stream_url)
        self.player.set_media(media)
        self.player.play()
        self.playing = True

        if (song_dict['source'] == '2'):
            self.now_playing_artist, self.now_playing_title = self.get_song_details(
                song_dict)
        else:
            self.now_playing_artist, self.now_playing_title = self.get_local_song_details(
                song_dict['trackId'])

        print("Playing...", self.now_playing_artist, " - ",
              self.now_playing_title)

    def stop(self):
        if self.player != None:
            self.player.stop()
            self.player = None
        self.playing = False
        self.repeat = Repeat.none
        self.random = False
        self.song_index = 0
        self.now_playing_title = ""
        self.now_playing_artist = ""

    def pause(self):
        if self.player == None:
            return False
        if self.playing == True:
            self.player.set_pause(1)
            self.playing = False
            return True
        return False

    def resume(self):
        if self.player == None:
            return False
        if self.playing == False:
            self.player.set_pause(0)
            self.playing = True
            return True
        return False

    def next(self):
        if self.player == None:
            return False
        # ランダム
        if self.random:
            self.song_index = random.randint(0, len(self.loaded_tracks) - 1)
            self.player.stop()
            self.play_song(self.loaded_tracks[self.song_index])
            event_manager = self.player.event_manager()
            event_manager.event_detach(EventType.MediaPlayerEndReached)
            event_manager.event_attach(EventType.MediaPlayerEndReached,
                                       self.end_callback, self.song_index + 1)
            return True
        # 通常
        if self.song_index + 1 < len(self.loaded_tracks):
            self.song_index += 1
            self.player.stop()
            self.play_song(self.loaded_tracks[self.song_index])
            event_manager = self.player.event_manager()
            event_manager.event_detach(EventType.MediaPlayerEndReached)
            event_manager.event_attach(EventType.MediaPlayerEndReached,
                                       self.end_callback, self.song_index + 1)
            return True
        else:
            if self.repeat == Repeat.playlist:
                self.start_playlist()
                return True

        return False

    def prev(self):
        if self.player == None:
            return False
        if self.song_index - 1 <= 0:
            self.song_index -= 1
            self.player.stop()
            self.play_song(self.loaded_tracks[self.song_index])
            event_manager = self.player.event_manager()
            event_manager.event_detach(EventType.MediaPlayerEndReached)
            event_manager.event_attach(EventType.MediaPlayerEndReached,
                                       self.end_callback, self.song_index + 1)
            return True
        return False

    def get_local_song_details(self, track_id):
        for song_dict in self.song_library:
            if track_id == song_dict['id']:
                return song_dict['artist'], song_dict['title']

    def get_song_details(self, song_dict):
        return song_dict['track']['albumArtist'], song_dict['track']['title']

    def set_volume(self, volume):
        if self.player:
            self.player.audio_set_volume(volume)
    if device['type'] == 'ANDROID':
        device_id = device['id'][2:] #.encode('ascii','ignore')
        break
    elif device['type'] == 'IOS':
        device_id = device['id']
        break

if not device_id:
    print("No Android or iOS device linked to account!")
    exit()

mc = Mobileclient()
mc.login(username, password, device_id)

# Grab all playlists, and sort them into a structure
playlists = mc.get_all_user_playlist_contents()
if not quiet:
    print(len(playlists), "playlist(s) found.")
master = []
for ply in playlists:
    name = ply['name']
    curPlaylist = Playlist(name)
    tracks = ply['tracks']
    for song in tracks:
        if song['source'] == u"2": # If song is not custom upload
            tid = song['trackId']
            title = song['track']['title']
            artist = song['track']['artist']
            album = song['track']['album']
            length = int(song['track']['durationMillis']) / 1000
            newSong = Song(tid, title, artist, album, length)
class GmusicComponent(MediaPlayerDevice):
    def __init__(self, hass, config):
        from gmusicapi import Mobileclient
        # https://github.com/simon-weber/gmusicapi/issues/424
        class GMusic(Mobileclient):
            def login(self, username, password, device_id, authtoken=None):
                if authtoken:
                    self.session._authtoken       = authtoken
                    self.session.is_authenticated = True
                    try:
                        # Send a test request to ensure our authtoken is still valide and working
                        self.get_registered_devices()
                        return True
                    except:
                        # Faild with the test-request so we set "is_authenticated=False"
                        # and go through the login-process again to get a new "authtoken"
                        self.session.is_authenticated = False
                if device_id:
                    if super(GMusic, self).login(username, password, device_id):
                        return True
                # Prevent further execution in case we failed with the login-process
                raise Exception("Legacy login failed! Please check logs for any gmusicapi related WARNING")

        self.hass = hass
        #self._api = GMusic()
        self._api = Mobileclient()

        _login_type = config.get(CONF_LOGIN_TYPE, DEFAULT_LOGIN_TYPE)
        _device_id = config.get(CONF_DEVICE_ID)

        if _login_type == 'legacy':
            _authtoken = config.get(CONF_TOKEN_PATH, DEFAULT_TOKEN_PATH) + "gmusic_authtoken"
            if os.path.isfile(_authtoken):
                with open(_authtoken, 'rb') as handle:
                    authtoken = pickle.load(handle)
            else:
                authtoken = None
            _username = config.get(CONF_USERNAME)
            _password = config.get(CONF_PASSWORD, DEFAULT_PASSWORD)
            logged_in = self._api.login(_username, _password, _device_id, authtoken)
            if not logged_in:
                _LOGGER.error("Failed legacy log in, check http://unofficial-google-music-api.readthedocs.io/en/latest/reference/mobileclient.html#gmusicapi.clients.Mobileclient.login")
                return False
            with open(_authtoken, 'wb') as f:
                pickle.dump(self._api.session._authtoken, f)

        elif _login_type == 'oauth':
            _oauth_cred = config.get(CONF_OAUTH_CRED, DEFAULT_OAUTH_CRED)
            if os.path.isfile(_oauth_cred):
                try:
                    logged_in = self._api.oauth_login(_device_id, _oauth_cred)
                    if not logged_in:
                        raise Exception("Login failed! Please check logs for any gmusicapi related WARNING")
                except:
                    raise Exception("Failed oauth login, check https://unofficial-google-music-api.readthedocs.io/en/latest/reference/mobileclient.html#gmusicapi.clients.Mobileclient.perform_oauth")
            else:
                raise Exception("Invalid - Not a file! oauth_cred: ", _oauth_cred)

        else:
            raise Exception("Invalid! login_type: ", _login_type)

        self._name = "gmusic_player"
        self._playlist = "input_select." + config.get(CONF_PLAYLISTS, DEFAULT_PLAYLISTS)
        self._media_player = "input_select." + config.get(CONF_SPEAKERS, DEFAULT_SPEAKERS)
        self._station = "input_select." + config.get(CONF_STATIONS, DEFAULT_STATIONS)
        self._source = "input_select." + config.get(CONF_SOURCE, DEFAULT_SOURCE)

        self._entity_ids = []  ## media_players - aka speakers
        self._playlists = []
        self._playlist_to_index = {}
        self._stations = []
        self._station_to_index = {}
        self._tracks = []
        self._track = []
        self._attributes = {}
        self._next_track_no = 0

        hass.bus.listen_once(EVENT_HOMEASSISTANT_START, self._update_playlists)
        hass.bus.listen_once(EVENT_HOMEASSISTANT_START, self._update_stations)

        self._shuffle = config.get(CONF_SHUFFLE, DEFAULT_SHUFFLE)
        self._shuffle_mode = config.get(CONF_SHUFFLE_MODE, DEFAULT_SHUFFLE_MODE)

        self._unsub_tracker = None
        self._playing = False
        self._state = STATE_OFF
        self._volume = 0.0
        self._is_mute = False
        self._track_name = None
        self._track_artist = None
        self._track_album_name = None
        self._track_album_cover = None
        self._track_artist_cover = None
        self._attributes['_player_state'] = STATE_OFF

    @property
    def name(self):
        """ Return the name of the player. """
        return self._name

    @property
    def icon(self):
        return 'mdi:music-circle'

    @property
    def supported_features(self):
        """ Flag media player features that are supported. """
        return SUPPORT_GMUSIC_PLAYER

    @property
    def should_poll(self):
        """ No polling needed. """
        return False

    @property
    def state(self):
        """ Return the state of the device. """
        return self._state

    @property
    def device_state_attributes(self):
        """ Return the device state attributes. """
        return self._attributes

    @property
    def is_volume_muted(self):
        """ Return True if device is muted """
        return self._is_mute

    @property
    def is_on(self):
        """ Return True if device is on. """
        return self._playing

    @property
    def media_content_type(self):
        """ Content type of current playing media. """
        return MEDIA_TYPE_MUSIC

    @property
    def media_title(self):
        """ Title of current playing media. """
        return self._track_name

    @property
    def media_artist(self):
        """ Artist of current playing media """
        return self._track_artist

    @property
    def media_album_name(self):
        """ Album name of current playing media """
        return self._track_album_name

    @property
    def media_image_url(self):
        """ Image url of current playing media. """
        return self._track_album_cover

    @property
    def media_image_remotely_accessible(self):
        " True  --> entity_picture: http://lh3.googleusercontent.com/Ndilu... "
        " False --> entity_picture: /api/media_player_proxy/media_player.gmusic_player?token=4454... "
        return True

    @property
    def shuffle(self):
        """Boolean if shuffling is enabled."""
        return self._shuffle

    @property
    def volume_level(self):
      """Volume level of the media player (0..1)."""
      return self._volume


    def turn_on(self, *args, **kwargs):
        """ Turn on the selected media_player from input_select """
        self._playing = False
        if not self._update_entity_ids():
            return
        _player = self.hass.states.get(self._entity_ids)
        data = {ATTR_ENTITY_ID: _player.entity_id}
        if _player.state == STATE_OFF:
            self._unsub_tracker = track_state_change(self.hass, _player.entity_id, self._sync_player)
            self._turn_on_media_player(data)
        elif _player.state != STATE_OFF:
            self._turn_off_media_player(data)
            call_later(self.hass, 1, self.turn_on)

    def _turn_on_media_player(self, data=None):
        """Fire the on action."""
        if data is None:
            data = {ATTR_ENTITY_ID: self._entity_ids}
        self._state = STATE_IDLE
        self.schedule_update_ha_state()
        self.hass.services.call(DOMAIN_MP, 'turn_on', data)


    def turn_off(self, entity_id=None, old_state=None, new_state=None, **kwargs):
        """ Turn off the selected media_player """
        self._playing = False
        self._track_name = None
        self._track_artist = None
        self._track_album_name = None
        self._track_album_cover = None

        _player = self.hass.states.get(self._entity_ids)
        data = {ATTR_ENTITY_ID: _player.entity_id}
        self._turn_off_media_player(data)

    def _turn_off_media_player(self, data=None):
        """Fire the off action."""
        self._playing = False
        self._state = STATE_OFF
        self._attributes['_player_state'] = STATE_OFF
        self.schedule_update_ha_state()
        if data is None:
            data = {ATTR_ENTITY_ID: self._entity_ids}
        self.hass.services.call(DOMAIN_MP, 'turn_off', data)


    def _update_entity_ids(self):
        """ sets the current media_player from input_select """
        media_player = self.hass.states.get(self._media_player)
        if media_player is None:
            _LOGGER.error("(%s) is not a valid input_select entity.", self._media_player)
            return False
        _entity_ids = "media_player." + media_player.state
        if self.hass.states.get(_entity_ids) is None:
            _LOGGER.error("(%s) is not a valid media player.", media_player.state)
            return False
        # Example: self._entity_ids = media_player.bedroom_stereo
        self._entity_ids = _entity_ids
        return True


    def _sync_player(self, entity_id=None, old_state=None, new_state=None):
        """ Perform actions based on the state of the selected media_player """
        # self._unsub_tracker = track_state_change(self.hass, self._entity_ids, self._sync_player)
        if not self._playing:
            return
        _player = self.hass.states.get(self._entity_ids)

        """ full state of device _player, include attributes. """
        #self._attributes['_player_full'] = _player

        """ entity_id of _player. """
        _player_id = _player.entity_id
        self._attributes['_player_id'] = _player_id

        """ _player "friendley_name" """
        _player_friendly = _player.attributes['friendly_name']
        self._attributes['_player_friendly'] = _player_friendly

        """ _player state - Example [playing -or- idle]. """
        _player_state = _player.state
        self._attributes['_player_state'] = _player_state

        """ Set new volume if it has been changed on the _player """
        if 'volume_level' in _player.attributes:
            self._volume = round(_player.attributes['volume_level'],2)

        if _player.state == 'off':
            self._state = STATE_OFF
            self.turn_off()

        self.schedule_update_ha_state()


    def _update_playlists(self, now=None):
        """ Sync playlists from Google Music library """
        self._playlist_to_index = {}
        self._playlists = self._api.get_all_user_playlist_contents()
        idx = -1
        for playlist in self._playlists:
            idx = idx + 1
            name = playlist.get('name','')
            if len(name) < 1:
                continue
            self._playlist_to_index[name] = idx

        playlists = list(self._playlist_to_index.keys())
        self._attributes['playlists'] = playlists

        data = {"options": list(playlists), "entity_id": self._playlist}
        self.hass.services.call(input_select.DOMAIN, input_select.SERVICE_SET_OPTIONS, data)


    def _update_stations(self, now=None):
        """ Sync stations from Google Music library """
        self._station_to_index = {}
        self._stations = self._api.get_all_stations()
        idx = -1
        for station in self._stations:
            idx = idx + 1
            name = station.get('name','')
            library = station.get('inLibrary')
            if len(name) < 1:
                continue
            if library == True:
                self._station_to_index[name] = idx

        stations = list(self._station_to_index.keys())
        stations.insert(0,"I'm Feeling Lucky")
        self._attributes['stations'] = stations

        data = {"options": list(stations), "entity_id": self._station}
        self.hass.services.call(input_select.DOMAIN, input_select.SERVICE_SET_OPTIONS, data)


    def _load_playlist(self, playlist=None):
        """ Load selected playlist to the track_queue """
        if not self._update_entity_ids():
            return
        """ if source == Playlist """
        _playlist_id = self.hass.states.get(self._playlist)
        if _playlist_id is None:
            _LOGGER.error("(%s) is not a valid input_select entity.", self._playlist)
            return
        if playlist is None:
            playlist = _playlist_id.state
        idx = self._playlist_to_index.get(playlist)
        if idx is None:
            _LOGGER.error("playlist to index is none!")
            self._turn_off_media_player()
            return
        self._tracks = None
        self._tracks = self._playlists[idx]['tracks']
        self._total_tracks = len(self._tracks)
        #self.log("Loading [{}] Tracks From: {}".format(len(self._tracks), _playlist_id))
        if self._shuffle and self._shuffle_mode != 2:
            random.shuffle(self._tracks)
        self._play()


    def _load_station(self, station=None):
        """ Load selected station to the track_queue """
        self._total_tracks = 100
        if not self._update_entity_ids():
            return
        """ if source == station """
        self._tracks = None
        _station_id = self.hass.states.get(self._station)
        if _station_id is None:
            _LOGGER.error("(%s) is not a valid input_select entity.", self._station)
            return
        if station is None:
            station = _station_id.state
        if station == "I'm Feeling Lucky":
            self._tracks = self._api.get_station_tracks('IFL', num_tracks = self._total_tracks)
        else:
            idx = None
            idx = self._station_to_index.get(station)
            if idx is None:
                self._turn_off_media_player()
                return
            _id = self._stations[idx]['id']
            self._tracks = self._api.get_station_tracks(_id, num_tracks = self._total_tracks)
        # self.log("Loading [{}] Tracks From: {}".format(len(self._tracks), _station_id))
        self._play()

    def _play(self):
        self._playing = True
        self._next_track_no = -1
        self._get_track()

    def _get_track(self, entity_id=None, old_state=None, new_state=None, retry=3):
        """ Get a track and play it from the track_queue. """
        _track = None
        if self._shuffle and self._shuffle_mode != 1:
            self._next_track_no = random.randrange(self._total_tracks) - 1
        else:
            self._next_track_no = self._next_track_no + 1
            if self._next_track_no >= self._total_tracks:
                self._next_track_no = 0         ## Restart curent playlist (Loop)
                #random.shuffle(self._tracks)    ## (re)Shuffle on Loop
        try:
            _track = self._tracks[self._next_track_no]
        except IndexError:
            _LOGGER.error("Out of range! Number of tracks in track_queue == (%s)", self._total_tracks)
            self._turn_off_media_player()
            return
        if _track is None:
            _LOGGER.error("_track is None!")
            self._turn_off_media_player()
            return
        """ If source is a playlist, track is inside of track """
        if 'track' in _track:
            _track = _track['track']
        """ Find the unique track id. """
        uid = ''
        if 'trackId' in _track:
            uid = _track['trackId']
        elif 'storeId' in _track:
            uid = _track['storeId']
        elif 'id' in _track:
            uid = _track['id']
        else:
            _LOGGER.error("Failed to get ID for track: (%s)", _track)
            if retry < 1:
                self._turn_off_media_player()
                return
            return self._get_track(retry=retry-1)
        """ If available, get track information. """
        if 'title' in _track:
            self._track_name = _track['title']
        else:
            self._track_name = None
        if 'artist' in _track:
            self._track_artist = _track['artist']
        else:
            self._track_artist = None
        if 'album' in _track:
            self._track_album_name = _track['album']
        else:
            self._track_album_name = None
        if 'albumArtRef' in _track:
            _album_art_ref = _track['albumArtRef']   ## returns a list
            self._track_album_cover = _album_art_ref[0]['url'] ## of dic
        else:
            self._track_album_cover = None
        if 'artistArtRef' in _track:
            _artist_art_ref = _track['artistArtRef']
            self._track_artist_cover = _artist_art_ref[0]['url']
        else:
            self._track_artist_cover = None
        """ Get the stream URL and play on media_player """
        try:
            _url = self._api.get_stream_url(uid)
        except Exception as err:
            _LOGGER.error("Failed to get URL for track: (%s)", uid)
            if retry < 1:
                self._turn_off_media_player()
                return
            return self._get_track(retry=retry-1)
        self._state = STATE_PLAYING
        self.schedule_update_ha_state()
        data = {
            ATTR_MEDIA_CONTENT_ID: _url,
            ATTR_MEDIA_CONTENT_TYPE: "audio/mp3",
            ATTR_ENTITY_ID: self._entity_ids
            }
        self.hass.services.call(DOMAIN_MP, SERVICE_PLAY_MEDIA, data)


    def play_media(self, media_type, media_id, **kwargs):
        if not self._update_entity_ids():
            return
        _player = self.hass.states.get(self._entity_ids)

        if media_type == "station":
            _source = {"option":"Station", "entity_id": self._source}
            _option = {"option": media_id, "entity_id": self._station}
            self.hass.services.call(input_select.DOMAIN, input_select.SERVICE_SELECT_OPTION, _source)
            self.hass.services.call(input_select.DOMAIN, input_select.SERVICE_SELECT_OPTION, _option)
        elif media_type == "playlist":
            _source = {"option":"Playlist", "entity_id": self._source}
            _option = {"option": media_id, "entity_id": self._playlist}
            self.hass.services.call(input_select.DOMAIN, input_select.SERVICE_SELECT_OPTION, _source)
            self.hass.services.call(input_select.DOMAIN, input_select.SERVICE_SELECT_OPTION, _option)
        else:
            _LOGGER.error("Invalid: (%s) --> media_types are 'station' or 'playlist'.", media_type)
            return

        if self._playing == True:
            self.media_stop()
            self.media_play()
        elif self._playing == False and self._state == STATE_OFF:
            if _player.state == STATE_OFF:
                self.turn_on()
            else:
                data = {ATTR_ENTITY_ID: _player.entity_id}
                self._turn_off_media_player(data)
                call_later(self.hass, 1, self.turn_on)
        else:
            _LOGGER.error("self._state is: (%s).", self._state)

    def media_play(self, entity_id=None, old_state=None, new_state=None, **kwargs):
        """Send play command."""
        if self._state == STATE_PAUSED:
            self._state = STATE_PLAYING
            self.schedule_update_ha_state()
            data = {ATTR_ENTITY_ID: self._entity_ids}
            self.hass.services.call(DOMAIN_MP, 'media_play', data)
        else:
            _source = self.hass.states.get(self._source)
            source = _source.state
            if source == 'Playlist':
                self._load_playlist()
            elif source == 'Station':
                self._load_station()
            else:
                _LOGGER.error("Invalid source: (%s)", source)
                self.turn_off()
                return

    def media_pause(self, **kwargs):
        """ Send media pause command to media player """
        self._state = STATE_PAUSED
        self.schedule_update_ha_state()
        data = {ATTR_ENTITY_ID: self._entity_ids}
        self.hass.services.call(DOMAIN_MP, 'media_pause', data)

    def media_play_pause(self, **kwargs):
        """Simulate play pause media player."""
        if self._state == STATE_PLAYING:
            self.media_pause()
        else:
            self.media_play()

    def media_previous_track(self, **kwargs):
        """Send the previous track command."""
        if self._playing:
            self._next_track_no = max(self._next_track_no - 2, -1)
            self._get_track()

    def media_next_track(self, **kwargs):
        """Send next track command."""
        if self._playing:
            self._get_track()

    def media_stop(self, **kwargs):
        """Send stop command."""
        self._state = STATE_IDLE
        self._playing = False
        self._track_artist = None
        self._track_album_name = None
        self._track_name = None
        self._track_album_cover = None
        self.schedule_update_ha_state()
        data = {ATTR_ENTITY_ID: self._entity_ids}
        self.hass.services.call(DOMAIN_MP, 'media_stop', data)

    def set_shuffle(self, shuffle):
        self._shuffle = shuffle
        if self._shuffle_mode == 1:
            self._attributes['shuffle_mode'] = 'Shuffle'
        elif self._shuffle_mode == 2:
            self._attributes['shuffle_mode'] = 'Random'
        elif self._shuffle_mode == 3:
            self._attributes['shuffle_mode'] = 'Shuffle Random'
        else:
            self._attributes['shuffle_mode'] = self._shuffle_mode
        return self.schedule_update_ha_state()

    def set_volume_level(self, volume):
        """Set volume level."""
        self._volume = round(volume,2)
        data = {ATTR_ENTITY_ID: self._entity_ids, 'volume_level': self._volume}
        self.hass.services.call(DOMAIN_MP, 'volume_set', data)
        self.schedule_update_ha_state()

    def volume_up(self, **kwargs):
        """Volume up the media player."""
        newvolume = min(self._volume + 0.05, 1)
        self.set_volume_level(newvolume)

    def volume_down(self, **kwargs):
        """Volume down media player."""
        newvolume = max(self._volume - 0.05, 0.01)
        self.set_volume_level(newvolume)

    def mute_volume(self, mute):
        """Send mute command."""
        if self._is_mute == False:
            self._is_mute = True
        else:
            self._is_mute = False
        self.schedule_update_ha_state()
        data = {ATTR_ENTITY_ID: self._entity_ids, "is_volume_muted": self._is_mute}
        self.hass.services.call(DOMAIN_MP, 'volume_mute', data)
Example #23
0
class GMusicWrapper(object):
    def __init__(self, username, password, logger=None):
        self._api = Mobileclient()
        self.logger = logger
        success = self._api.login(
            username, password,
            getenv('ANDROID_ID', Mobileclient.FROM_MAC_ADDRESS))

        if not success:
            raise Exception("Unsuccessful login. Aborting!")

        # Populate our library
        self.library = {}
        self.indexing_thread = threading.Thread(target=self.index_library)
        self.indexing_thread.start()

    def log(self, log_str):
        self.logger.debug(log_str)

    def _search(self, query_type, query):
        try:
            results = self._api.search(query)
        except CallFailure:
            return []

        hits_key = "%s_hits" % query_type

        if hits_key not in results:
            return []

        # Ugh, Google had to make this schema nonstandard...
        if query_type == 'song':
            query_type = 'track'

        return [x[query_type] for x in results[hits_key]]

    def is_indexing(self):
        return self.indexing_thread.is_alive()

    def index_library(self):
        """
        Downloads the a list of every track in a user's library and populates
        self.library with storeIds -> track definitions
        """
        self.log('Fetching library...')

        tracks = self.get_all_songs()

        for track in tracks:
            song_id = track['id']
            self.library[song_id] = track

        self.log('Fetching library...')

    def get_artist(self, name):
        """
        Fetches information about an artist given its name
        """
        search = self._search("artist", name)

        if len(search) == 0:
            return False

        return self._api.get_artist_info(search[0]['artistId'],
                                         max_top_tracks=100)

    def get_album(self, name, artist_name=None):
        if artist_name:
            name = "%s %s" % (name, artist_name)

        search = self._search("album", name)

        if len(search) == 0:
            return False

        return self._api.get_album_info(search[0]['albumId'])

    def get_latest_album(self, artist_name=None):
        search = self._search("artist", artist_name)

        if len(search) == 0:
            return False

        artist_info = self._api.get_artist_info(search[0]['artistId'],
                                                include_albums=True)
        album_info = artist_info['albums']
        sorted_list = sorted(album_info.__iter__(),
                             key=lambda s: s['year'],
                             reverse=True)

        for index, val in enumerate(sorted_list):
            album_info = self._api.get_album_info(
                album_id=sorted_list[index]['albumId'], include_tracks=True)
            if len(album_info['tracks']) >= 5:
                return album_info

        return False

    def get_album_by_artist(self, artist_name, album_id=None):
        search = self._search("artist", artist_name)
        if len(search) == 0:
            return False

        artist_info = self._api.get_artist_info(search[0]['artistId'],
                                                include_albums=True)
        album_info = artist_info['albums']
        random.shuffle(album_info)

        for index, val in enumerate(album_info):
            album = self._api.get_album_info(
                album_id=album_info[index]['albumId'], include_tracks=True)
            if album['albumId'] != album_id and len(album['tracks']) >= 5:
                return album

        return False

    def get_song(self, name, artist_name=None, album_name=None):
        if artist_name:
            name = "%s %s" % (artist_name, name)
        elif album_name:
            name = "%s %s" % (album_name, name)

        search = self._search("song", name)

        if len(search) == 0:
            return False

        if album_name:
            for i in range(0, len(search) - 1):
                if album_name in search[i]['album']:
                    return search[i]
        return search[0]

    def get_promoted_songs(self):
        return self._api.get_promoted_songs()

    def get_station(self, title, track_id=None, artist_id=None, album_id=None):
        if artist_id is not None:
            if album_id is not None:
                if track_id is not None:
                    return self._api.create_station(title, track_id=track_id)
                return self._api.create_station(title, album_id=album_id)
            return self._api.create_station(title, artist_id=artist_id)

    def get_station_tracks(self, station_id):
        return self._api.get_station_tracks(station_id)

    def get_google_stream_url(self, song_id):
        return self._api.get_stream_url(song_id)

    def get_stream_url(self, song_id):
        return "%s/alexa/stream/%s" % (getenv('APP_URL'), song_id)

    def get_thumbnail(self, artist_art):
        return artist_art.replace("http://", "https://")

    def get_all_user_playlist_contents(self):
        return self._api.get_all_user_playlist_contents()

    def get_all_songs(self):
        return self._api.get_all_songs()

    def rate_song(self, song, rating):
        return self._api.rate_songs(song, rating)

    def extract_track_info(self, track):
        # When coming from a playlist, track info is nested under the "track"
        # key
        if 'track' in track:
            track = track['track']

        if 'storeId' in track:
            return track, track['storeId']
        elif 'trackId' in track:
            return self.library[track['trackId']], track['trackId']
        else:
            return None, None

    def get_artist_album_list(self, artist_name):
        search = self._search("artist", artist_name)
        if len(search) == 0:
            return "Unable to find the artist you requested."

        artist_info = self._api.get_artist_info(search[0]['artistId'],
                                                include_albums=True)
        album_list_text = "Here's the album listing for %s: " % artist_name

        counter = 0
        for index, val in enumerate(artist_info['albums']):
            if counter > 25:  # alexa will time out after 10 seconds if the list takes too long to iterate through
                break
            album_info = self._api.get_album_info(
                album_id=artist_info['albums'][index]['albumId'],
                include_tracks=True)
            if len(album_info['tracks']) > 5:
                counter += 1
                album_list_text += (
                    artist_info['albums'][index]['name']) + ", "
        return album_list_text

    def get_latest_artist_albums(self, artist_name):
        search = self._search("artist", artist_name)

        if len(search) == 0:
            return False

        artist_info = self._api.get_artist_info(search[0]['artistId'],
                                                include_albums=True)
        album_list = artist_info['albums']

        sorted_list = sorted(album_list.__iter__(),
                             key=lambda s: s['year'],
                             reverse=True)

        speech_text = 'The latest albums by %s are ' % artist_name

        counter = 0
        for index, val in enumerate(sorted_list):
            if counter > 5:
                break
            else:
                album_info = self._api.get_album_info(
                    album_id=sorted_list[index]['albumId'],
                    include_tracks=True)
                if len(album_info['tracks']) >= 5:
                    counter += 1
                    album_name = sorted_list[index]['name']
                    album_year = sorted_list[index]['year']
                    speech_text += '%s, released in %d, ' % (album_name,
                                                             album_year)

        return speech_text

    def closest_match(self,
                      request_name,
                      all_matches,
                      artist_name='',
                      minimum_score=70):
        # Give each match a score based on its similarity to the requested
        # name
        self.log('Fetching library...')

        request_name = request_name.lower() + artist_name.lower()
        scored_matches = []
        for i, match in enumerate(all_matches):
            try:
                name = match['name'].lower()
            except (KeyError, TypeError):
                i = match
                name = all_matches[match]['title'].lower()
                if artist_name != "":
                    name += all_matches[match]['artist'].lower()

            scored_matches.append({
                'index': i,
                'name': name,
                'score': fuzz.ratio(name, request_name)
            })

        sorted_matches = sorted(scored_matches,
                                key=lambda a: a['score'],
                                reverse=True)
        top_scoring = sorted_matches[0]
        self.log('Fetching library...')

        best_match = all_matches[top_scoring['index']]

        # Make sure we have a decent match (the score is n where 0 <= n <= 100)
        if top_scoring['score'] < minimum_score:
            return None

        return best_match

    def get_genres(self, parent_genre_id=None):
        return self._api.get_genres(parent_genre_id)

    def increment_song_playcount(self, song_id, plays=1, playtime=None):
        return self._api.increment_song_playcount(song_id, plays, playtime)

    def get_song_data(self, song_id):
        return self._api.get_track_info(song_id)

    @classmethod
    def generate_api(cls, **kwargs):
        return cls(getenv('GOOGLE_EMAIL'), getenv('GOOGLE_PASSWORD'), **kwargs)
Example #24
0
class GPMClient(object):
    """
    Google Play Music client.
    """

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

#------------------------------------------------------------------------------

    def __init__(self, email, password, device_id):
        self.__api = Mobileclient()
        self.logged_in = False
        self.__device_id = device_id

        attempts = 0
        while not self.logged_in and attempts < 3:
            self.logged_in = self.__api.login(email, password, device_id)
            attempts += 1

        self.all_tracks = dict()
        self.playlists = dict()
        self.library = dict()

#------------------------------------------------------------------------------

    def logout(self):
        self.__api.logout()

#------------------------------------------------------------------------------

    def update_local_lib(self):
        songs = self.__api.get_all_songs()
        self.playlists[self.thumbs_up_playlist_name] = list()

        # Get main library
        song_map = dict()
        for song in songs:
            if "rating" in song and song["rating"] == "5":
                self.playlists[self.thumbs_up_playlist_name].append(song)

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

            song_map[song_id] = song

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

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

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

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

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

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

        # Get all playlists
        plists = self.__api.get_all_user_playlist_contents()
        for plist in plists:
            plist_name = plist["name"]
            self.playlists[plist_name] = list()
            for track in plist["tracks"]:
                if track["trackId"] not in song_map:
                    song = song_map[track["trackId"]] = track["track"]
                    song["id"] = track["trackId"]
                else:
                    song = song_map[track["trackId"]]
                self.playlists[plist_name].append(song)

#------------------------------------------------------------------------------

    def get_stream_url(self, song):
        return self.__api.get_stream_url(song["id"], self.__device_id)

#------------------------------------------------------------------------------

    def rate_song(self, song, rating):
        try:
            song["rating"] = rating
            song_list = [song]
            self.__api.change_song_metadata(song_list)
            print "Gave a Thumbs Up to {0} by {1} on Google Play.".format(
                   song["title"].encode("utf-8"),
                   song["artist"].encode("utf-8"))
        except RuntimeError:
            print "Error giving a Thumbs Up on Google Play."
Example #25
0
class GMusicSession(object):

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

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

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

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

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

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

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

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

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

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

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

        self.boolean_connect = self.args["boolean_connect"]
        self.boolean_sync = self.args["boolean_sync"]
        self.boolean_power = self.args["boolean_power"]
        self.select_player = self.args["select_player"]
        self.select_playlist = self.args["select_playlist"]
        self.select_station = self.args["select_station"]
        self.boolean_load_pl = self.args["boolean_load_pl"]
        self.select_source = self.args["select_source"]
        self.boolean_next = self.args["boolean_next"]
        self.boolean_prev = self.args["boolean_prev"]

        self.turn_off(self.boolean_connect)

        self._player_id = ''
        self._playlist_id = ''
        self._playlists = []
        self._playlist_to_index = {}
        self._station_id = ''
        self._stations = []
        self._station_to_index = {}
        self._tracks = []
        self._next_track_no = 0
        self._track = []

        self._source = self.get_state(self.select_source)

        self.listen_state(self.connect, self.boolean_connect)
        self.listen_state(self.power, self.boolean_power, new="on")
        self.listen_state(self.sync, self.boolean_sync, new="on")
        self.listen_state(self.set_source, self.select_source)
        self.listen_state(self.get_tracks, self.boolean_load_pl, new="on")
        self.listen_state(self.next_track, self.boolean_next, new="on")
        self.listen_state(self.prev_track, self.boolean_prev, new="on")

        self.turn_on(self.boolean_connect)

    def reset_booleans(self):
        '''
    Do not include "boolean_connect" here!
    - NOT HERE: self.turn_off(self.boolean_connect)
    That should be turned off directly when needed
    '''
        self.turn_off(self.boolean_load_pl)
        self.turn_off(self.boolean_sync)
        self.turn_off(self.boolean_power)
        self.turn_off(self.boolean_next)
        self.turn_off(self.boolean_prev)

    def login(self):
        _login = self.args["login_type"]
        if _login == 'oauth':
            self.oauth_login()
        elif _login == 'legacy':
            self.legacy_login()
        else:
            self.turn_off(self.boolean_connect)
            raise SystemExit("Invalid login_type: {}".format(_login))

    def legacy_login(self):
        ''' This legacy login may stop working at any time '''
        authtoken_path = self.args["authtoken_path"] + "gmusic_authtoken"
        email = self.args["user"]
        password = self.args["password"]
        device_id = self.args["device_id"]
        if os.path.isfile(authtoken_path):
            with open(authtoken_path, 'rb') as handle:
                authtoken = pickle.load(handle)
        else:
            authtoken = None
        self._api_login = self.gmc.login(email, password, device_id, authtoken)
        if not self._api_login:
            self.turn_off(self.boolean_connect)
            raise SystemExit("legacy login failed")
        with open(authtoken_path, 'wb') as f:
            pickle.dump(self.gmc.session._authtoken, f)

    def oauth_login(self):
        if self.args["oauth_credentials"]:
            try:
                self._api_login = self.gmc.oauth_login(
                    device_id=self.args["device_id"],
                    oauth_credentials=self.args["oauth_credentials"],
                    locale=self.args["locale"])
            except:
                self.turn_off(self.boolean_connect)
                raise SystemExit("oauth login failed")

    def connect(self, entity, attribute, old, new, kwargs):
        _connect = self.get_state(self.boolean_connect)
        self.reset_booleans()
        if _connect == "on":
            self.login()
            if self._api_login == True:
                self.sync(entity=None,
                          attribute=None,
                          old=None,
                          new=None,
                          kwargs=None)
        else:  ## This will not trigger when app level constaint is set on the input_boolean
            self.gmusic_api_logout(entity=None,
                                   attribute=None,
                                   old=None,
                                   new=None,
                                   kwargs=None)

    def power(self, entity, attribute, old, new, kwargs):
        self._power = self.get_state(self.boolean_power)
        if self._power == "on":
            self.select_media_player()
            self.log("Powered ON -- Connected: {}".format(self._player_id))
        elif self._power == "off":
            self.unselect_media_player()
            self.log("Powered OFF -- Disconnected: {}".format(self._player_id))

    def select_media_player(self):
        self._player_id = "media_player." + self.get_state(self.select_player)
        ## Set callbacks for media player
        self.power_off = self.listen_state(self.power,
                                           self.boolean_power,
                                           new="off",
                                           duration="1")
        self.advance_track = self.listen_state(self.get_track,
                                               self._player_id,
                                               new="idle",
                                               duration="2")
        self.show_meta = self.listen_state(self.show_info,
                                           self._player_id,
                                           new="playing")
        self.clear_meta = self.listen_state(self.clear_info,
                                            self._player_id,
                                            new="off",
                                            duration="1")

    def unselect_media_player(self):
        self.media_player_off(entity=None,
                              attribute=None,
                              old=None,
                              new=None,
                              kwargs=None)
        try:  ## Cancel callbacks for media player
            self.cancel_listen_state(self.power_off)
            self.cancel_listen_state(self.advance_track)
            self.cancel_listen_state(self.show_meta)
            self.cancel_listen_state(self.clear_meta)
        except:
            self.log("cancel callback exception!")
            pass

    def update_playlists(self, entity, attribute, old, new, kwargs):
        self._playlist_to_index = {}
        self._playlists = self.gmc.get_all_user_playlist_contents()
        idx = -1
        for playlist in self._playlists:
            idx = idx + 1
            name = playlist.get('name', ' ')
            if len(name) < 1:
                continue
            self._playlist_to_index[name] = idx
            # self.log("Playlist: {} - {}".format(idx, name))
        data = list(self._playlist_to_index.keys())
        self.call_service("input_select/set_options",
                          entity_id=self.select_playlist,
                          options=data)
        self.turn_off(self.boolean_sync)

        self.log("--------------------------------------------")
        self.log(data)
        for _pl in self._playlist_to_index:
            _num = self._playlist_to_index.get(_pl) + 1
            self.log("{}: {}".format(_num, _pl))
        self.log("--------------------------------------------")

    def update_stations(self, entity, attribute, old, new, kwargs):
        self._station_to_index = {}
        self._stations = self.gmc.get_all_stations()
        idx = -1
        for station in self._stations:
            idx = idx + 1
            name = station.get('name', ' ')
            library = station.get('inLibrary')
            if len(name) < 1:
                continue
            if library == True:
                self._station_to_index[name] = idx
                # self.log("station: {} - {}: Library = {}".format(idx, name, library))
        data = list(self._station_to_index.keys())
        data.insert(0, "I'm Feeling Lucky")
        self.call_service("input_select/set_options",
                          entity_id=self.select_station,
                          options=data)
        self.turn_off(self.boolean_sync)

        self.log("--------------------------------------------")
        self.log(data)
        for _pl in self._station_to_index:
            _num = self._station_to_index.get(_pl) + 1
            self.log("{}: {}".format(_num, _pl))
        self.log("--------------------------------------------")

    def sync(self, entity, attribute, old, new, kwargs):
        self.update_playlists(entity=None,
                              attribute=None,
                              old=None,
                              new=None,
                              kwargs=None)
        self.update_stations(entity=None,
                             attribute=None,
                             old=None,
                             new=None,
                             kwargs=None)

    def set_source(self, entity, attribute, old, new, kwargs):
        self._source = self.get_state(self.select_source)

    def get_tracks(self, entity, attribute, old, new, kwargs):
        if self._source == 'Station':
            self.load_station(entity=None,
                              attribute=None,
                              old=None,
                              new=None,
                              kwargs=None)
        elif self._source == 'Playlist':
            self.load_playlist(entity=None,
                               attribute=None,
                               old=None,
                               new=None,
                               kwargs=None)
        else:
            self.log("invalid source: {}".format(self._source))
            self.reset_booleans()

    def load_playlist(self, entity, attribute, old, new, kwargs):
        self.turn_on(self.boolean_power)
        self._playlist_id = self.get_state(self.select_playlist)

        idx = self._playlist_to_index.get(self._playlist_id)
        self._tracks = self._playlists[idx]['tracks']
        random.shuffle(self._tracks)

        self.log("--------------------------------------------")
        self.log("Loading [{}] Tracks From: {}".format(len(self._tracks),
                                                       self._playlist_id))
        # self.log(self._tracks)
        self.log("--------------------------------------------")

        self._next_track_no = -1
        self.get_track(entity=None,
                       attribute=None,
                       old=None,
                       new=None,
                       kwargs=None)
        self.turn_off(self.boolean_load_pl)

    def load_station(self, entity, attribute, old, new, kwargs):
        self.turn_on(self.boolean_power)
        self._station_id = self.get_state(self.select_station)
        if self._station_id == "I'm Feeling Lucky":
            self._tracks = self.gmc.get_station_tracks('IFL', num_tracks=100)
        else:
            idx = self._station_to_index.get(self._station_id)
            id = self._stations[idx]['id']
            self._tracks = self.gmc.get_station_tracks(id, num_tracks=100)

        self.log("--------------------------------------------")
        self.log("Loading [{}] Tracks From: {}".format(len(self._tracks),
                                                       self._station_id))
        # self.log(self._tracks)
        self.log("--------------------------------------------")

        self._next_track_no = -1
        self.get_track(entity=None,
                       attribute=None,
                       old=None,
                       new=None,
                       kwargs=None)
        self.turn_off(self.boolean_load_pl)

    def get_track(self, entity, attribute, old, new, kwargs):
        self._track = ''
        self._next_track_no = self._next_track_no + 1
        if self._next_track_no >= len(self._tracks):
            self._next_track_no = 0  ## Restart curent playlist (Loop)
            random.shuffle(self._tracks)  ## (re)Shuffle on Loop

        _track = self._tracks[self._next_track_no]
        if _track is None:
            self.reset_booleans()
        if 'track' in _track:
            _track = _track['track']
        self._track = _track

        if 'trackId' in _track:
            _uid = _track['trackId']
        elif 'storeId' in _track:
            _uid = _track['storeId']
        elif 'id' in _track:
            _uid = _track['id']
        else:
            self.log("TRACK ID NOT FOUND!")

        self.play_track(_uid)

    def play_track(self, uid):
        try:
            _url = self.gmc.get_stream_url(uid)
            self.call_service("media_player/play_media",
                              entity_id=self._player_id,
                              media_content_id=_url,
                              media_content_type=self.args["media_type"])
        except:
            self.log(" --- FAILED TO PLAY TRACK --- ")
            self.log("uid: {}".format(uid))
            self.log("_url: {}".format(_url))
            # self.get_track(entity=None,attribute=None,old=None,new=None,kwargs=None)

    def next_track(self, entity, attribute, old, new, kwargs):
        if new == 'on':
            self.get_track(entity=None,
                           attribute=None,
                           old=None,
                           new=None,
                           kwargs=None)
            self.turn_off(self.boolean_next)

    def prev_track(self, entity, attribute, old, new, kwargs):
        if new == 'on':
            self._next_track_no = self._next_track_no - 2
            self.get_track(entity=None,
                           attribute=None,
                           old=None,
                           new=None,
                           kwargs=None)
            self.turn_off(self.boolean_prev)

    def show_info(self, entity, attribute, old, new, kwargs):
        _attr = {}
        _track = self._track
        if 'artist' in _track:
            _attr['media_artist'] = _track['artist']
        if 'album' in _track:
            _attr['media_album_name'] = _track['album']
        if 'title' in _track:
            _attr['media_title'] = _track['title']
        if 'albumArtRef' in _track:
            _album_art_ref = _track['albumArtRef']  ## returns a list
            _attr['entity_picture'] = _album_art_ref[0]['url']  ## of dic
        if 'artistArtRef' in _track:
            _artist_art_ref = _track['artistArtRef']
            self.artist_art = _artist_art_ref[0]['url']
        if _attr:
            self.set_state(self._player_id, attributes=_attr)

    def clear_info(self, entity, attribute, old, new, kwargs):
        if new != 'playing' or new != 'paused':
            self.set_state(self._player_id,
                           attributes={
                               "media_title": '',
                               "media_artist": '',
                               "media_album_name": '',
                               "entity_picture": ''
                           })

    def media_player_off(self, entity, attribute, old, new, kwargs):
        self.call_service("media_player/turn_off", entity_id=self._player_id)

    def gmusic_api_logout(self, entity, attribute, old, new, kwargs):
        self.gmc.logout()
        self.reset_booleans()
Example #27
0
            _temp_pl = _v.copy()
            _temp_pl['tracks'] = [self.lookup_track(_t_id) for _t_id in _v['tracks']]
            self._detailed_playlists[_k] = _temp_pl


keepers = ['Altivo', 'Runaway', 'Drives', 'Altivo 2.0', 'Slytherin', 'Now Showing: Anywhere But Here',
           'Altivo 2.5', 'HELL YEAH', 'Angst 2.0']

api = Mobileclient()

login_success = api.login(__CRED['GMusic']['username'], __CRED['GMusic']['password'], Mobileclient.FROM_MAC_ADDRESS)
assert login_success

new_col = GPlaylistCollection()

for playlist in api.get_all_user_playlist_contents():
    if playlist.get('name') in keepers:
        new_col.add_playlist(playlist)

for s in api.get_all_songs():
    if s.get('id') in new_col.all_track_ids:
        # This track belongs to one of the playlists
        new_col.define_track(s)

new_col.build_detailed_playlists()

found_tracks = []

for k_id in new_col.detailed_playlists.keys():
    cur_pl = new_col.detailed_playlists.get(k_id)
    cur_pl_name = cur_pl.get('name')
Example #28
0
class MusicLibrary(object):
    """This class reads information about your Google Play Music library"""

    def __init__(self, username=None, password=None,
                 true_file_size=False, scan=True, verbose=0):
        self.verbose = False
        if verbose > 1:
            self.verbose = True

        self.__login_and_setup(username, password)

        self.__artists = {}  # 'artist name' -> {'album name' : Album(), ...}
        self.__gartists = {}
        self.__albums = []  # [Album(), ...]
        self.__galbums = {}
        self.__tracks = {}
        self.__playlists = {}
        if scan:
            self.rescan()
        self.true_file_size = true_file_size

    def rescan(self):
        """Scan the Google Play Music library"""
        self.__artists = {}  # 'artist name' -> {'album name' : Album(), ...}
        self.__gartists = {}
        self.__albums = []  # [Album(), ...]
        self.__galbums = {}
        self.__tracks = {}
        self.__playlists = {}
        self.__aggregate_albums()

    def __login_and_setup(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)
            self.config = ConfigParser.ConfigParser()
            self.config.read(cred_path)
            username = self.config.get('credentials', 'username')
            password = self.config.get('credentials', 'password')
            global deviceId
            deviceId = self.config.get('credentials', 'deviceId')
            if not username or not password:
                raise NoCredentialException(
                    'No username/password could be read from config file'
                    ': %s' % cred_path)
            if not deviceId:
                raise NoCredentialException(
                    'No deviceId could be read from config file'
                    ': %s' % cred_path)
            if deviceId.startswith("0x"):
                deviceId = deviceId[2:]

        self.api = GoogleMusicAPI(debug_logging=self.verbose)
        log.info('Logging in...')
        self.api.login(username, password, deviceId)
        log.info('Login successful.')

    def __set_key_from_ginfo(self, track, ginfo, key, to_key=None):
        """Set track key from either album_info or artist_info"""
        if to_key is None:
            to_key = key

        try:
            int_key = int(key)
        except ValueError:
            int_key = None

        if (not track.has_key(key) or track[key] == "" or int_key == 0) and ginfo.has_key(to_key):
            track[key] = ginfo[to_key]

        return track

    def __aggregate_albums(self):
        """Get all the tracks and playlists in the library, parse into relevant dicts"""
        log.info('Gathering track information...')
        tracks = self.api.get_all_songs()
        for track in tracks:
            log.debug('track = %s' % pp.pformat(track))

            # Get album and artist information from Google
            if track.has_key('albumId'):
                if self.__galbums.has_key(track['albumId']):
                    album_info = self.__galbums[track['albumId']]
                else:
                    log.info("Downloading album info for %s '%s'", track['albumId'], track['album'])
                    try:
                        album_info = self.__galbums[track['albumId']] = self.api.get_album_info(track['albumId'], include_tracks=False)
                    except gmusicapi.exceptions.CallFailure:
                        log.exception("Failed to download album info for %s '%s'", track['albumId'], track['album'])
                        #album_info = {}
                if album_info.has_key('artistId') and len(album_info['artistId']) > 0 and album_info['artistId'][0] != "":
                    artist_id = album_info['artistId'][0]
                    if self.__gartists.has_key(artist_id):
                        artist_info = self.__gartists[artist_id]
                    else:
                        log.info("Downloading artist info for %s '%s'", artist_id, album_info['albumArtist'])
                        #if album_info['albumArtist'] == "Various":
                        #    print album_info
                        artist_info = self.__gartists[artist_id] = self.api.get_artist_info(artist_id, include_albums=False, max_top_tracks=0, max_rel_artist=0)
                else:
                    artist_info = {}
            else:
                album_info = {}
                artist_info = {}

            track = self.__set_key_from_ginfo(track, album_info, 'album', 'name')
            track = self.__set_key_from_ginfo(track, album_info, 'year')
            track = self.__set_key_from_ginfo(track, artist_info, 'albumArtist', 'name')

            # Prefer the album artist over the track artist if there is one
            artist_name = formatNames(track['albumArtist'])
            if artist_name.strip() == '':
                artist_name = formatNames(track['artist'])
            if artist_name.strip() == '':
                artist_name = 'Unknown'

            # Get the Artist object, or create one if it doesn't exist
            artist = self.__artists.get(artist_name.lower(), None)
            if not artist:
                artist = Artist(self, artist_name)
                self.__artists[artist_name.lower()] = artist

            # Get the Album object, or create one if it doesn't exist
            album = artist.get_album(formatNames(track['album']))
            if not album:
                album = Album(self, track['album'])
                self.__albums.append(album)  # NOTE: Current no purpose other than to count
                artist.add_album(album)

            # Add track to album
            album.add_track(track)

            # Add track to list of all tracks, indexable by track ID
            if 'id' in track:
                self.__tracks[track['id']] = track

        log.info('%d tracks loaded.' % len(tracks))
        log.info('%d artists loaded.' % len(self.__artists))
        log.info('%d albums loaded.' % len(self.__albums))

        # Add all playlists
        playlists = self.api.get_all_user_playlist_contents()
        for pldata in playlists:
            playlist = Playlist(self, pldata)
            self.__playlists[playlist.dirname.lower()] = playlist
        log.debug('%d playlists loaded.' % len(self.__playlists))

    def get_artists(self):
        """Return all artists in the library"""
        return self.__artists

    def get_artist(self, name):
        """Return the artist from the library with the specified name"""
        return self.__artists.get(name.lower(), None)

    def get_playlists(self):
        """Return list of all playlists in the library"""
        return self.__playlists.values()

    def get_playlist(self, name):
        """Return the playlist from the library with the specified name"""
        return self.__playlists.get(name.lower(), None)

    def get_track(self, trackid):
        """Return the track from the library with the specified track ID"""
        return self.__tracks.get(trackid, None)

    def cleanup(self):
        pass
Example #29
0
rfcTitle = data[['artist','title','energy','tempo','danceability','artist_discovery','speechiness','year','duration','trackType','acousticness','liveness','loudness','time_signature','valence','id','albumArtRef','storeId']]
rfcTitle = rfcTitle.dropna(subset = filter(lambda x: x != "albumArtRef", rfcTitle.columns))
rfc = rfcTitle[['energy','tempo','danceability','artist_discovery','speechiness','year','trackType','acousticness','liveness','loudness','time_signature','valence']]

km = pickle.load(open("/home/drew/tempo_scripts/tempo_model.p", "rb" ))
klbls = km.labels_
rfcTitle['label'] = klbls

results = []

for index, value in enumerate(rfcTitle.values):
    results.append({'artist': value[0], 'title': value[1], 'energy': value[2], 'tempo': value[3], 'danceability': value[4], 'artist_discovery': value[5], 'speechiness': value[6], 'year': value[7], 'duration': value[8], 'trackType': value[9], 'acousticness': value[10], 'liveness': value[11], 'loudness': value[12], 'time_signature': value[13], 'valence': value[14], 'cluster': value[18], 'id': value[15], 'albumArtRef': value[16], 'storeId': value[17]})

#playlists = api.get_all_playlists()
#print json.dumps(playlists, indent=4, separators=(',', ': '))
plsongs = api.get_all_user_playlist_contents()
#print json.dumps(plsongs, indent=4, separators=(',', ': '))

data = pd.read_csv("/home/drew/gpmusic_fixed.csv")
ret = {'results': []}

for i,v in enumerate(plsongs[0]['tracks']):
    for i2,v2 in enumerate(results):
        #print v2['storeId'], "==", plsongs[i]['track']['storeId']
        if v2['id'] == v['trackId']:
            ret['results'].append(v2)
            break
api.remove_entries_from_playlist("1c5aa722-db07-3801-8966-54b6cef43513")
print json.dumps(plsongs, indent=4, separators=(',', ': '))
print json.dumps(ret, indent=4, separators=(',', ': '))
class MusicSync(object):
    def __init__(self, email=None, password=None):
        self.mm = Musicmanager()
        self.wc = Webclient()
        self.mc = Mobileclient()
        if not email:
            email = raw_input("Email: ")
        if not password:
            password = getpass()

        self.email = email
        self.password = password

        self.logged_in = self.auth()

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


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

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

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

        print "Authenticated"
        print ""


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

        plid = ""

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

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

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

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

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

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

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

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

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

            if not song_id:
                failed_files += 1
                continue

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

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

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


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

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

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

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

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

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

    def get_platform_path(self, full_path):
        # Try to avoid messing with the path if possible
        if os.sep == '/' and '\\' not in full_path:
            return full_path
        if os.sep == '\\' and '\\' in full_path:
            return full_path
        if '\\' not in full_path:
            return full_path
        return os.path.normpath(full_path.replace('\\', '/'))
Example #31
0
# remove the table header.
del table[0]

song = Song()

api = Mobileclient()
logged_in = api.login(args.u, args.p)

if args.li:
    count = min(len(table), args.li)
else:
    count = len(table)

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

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

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

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

            if result['song_hits']:
Example #32
0
class GoogleMusicManager(object):
    def __init__(self):
        """Default construct"""  #Main management Script
        #This will scrape that data from a users account, peramiters can be set for downloading certains songs or entire playlists
        #metadata is preserved as well as playlist or albumn structure
        self.api = []
        self.activeSongList = []
        self.activePlayList = []

    def Login(self, userName, userPassWord):
        """Login method to access you google play account"""
        self.api = Mobileclient()
        self.api.logout()
        #reload(MusicLibraryManager)
        if (self.api.login(userName, userPassWord,
                           Mobileclient.FROM_MAC_ADDRESS)):

            print("You have been logged in")
        else:
            print("Seems the login is incorect")

    def getAllRadioStations(self):
        """This will get all of the radiostations that I have saved"""
        print("Collecting Radio Station Contents")
        #Generate base directory
        #check to see if a playlist directory exists
        baseDir = os.getcwd() + '/RadioStations'
        #if not os.path.exists(baseDir):
        #    raise ValueError(baseDir + " does not exist")

        self.activeRadioStations = self.api.get_all_stations()
        for Station in self.activeRadioStations:
            print("Found a RadioStation called : " + Station['name'])
            #get radioContent
            RadioStationSongs = self.api.get_station_tracks(
                Station['id'], 1000, {})
            print("It contains " + str(len(RadioStationSongs)) + " Songs")
            print("downloading songs")
            stationName = Station['name']
            #create a dir for the radiostation
            activeDir = baseDir + "/" + stationName + "/"
            #check to see if the radio station folder exists
            if not os.path.exists(activeDir):
                #radiostation doesn't exist so we create it
                os.makedirs(activeDir)

            for Song in RadioStationSongs:
                print(str(Song))

                if not os.path.exists(activeDir + Song['title'] + ".mp3"):
                    #it doesn't exists in the playlist yet, so we need to add it
                    songTitle = Song['title']
                    streamUrl = self.api.get_stream_url(Song['storeId'])
                    urllib.urlretrieve(streamUrl,
                                       activeDir + songTitle + '.mp3')
                    song = eyed3.load(activeDir + songTitle + '.mp3')
                    song.initTag()
                    song.tag.save()
                    song = eyed3.load(activeDir + songTitle + '.mp3')
                    if "title" in Song:
                        song.tag.title = Song['title']
                    if "artist" in Song:
                        song.tag.artist = Song['artist']
                    if "album" in Song:
                        song.tag.album = Song['album']
                    #song.tag.year = currentSong['year']

                    if "genre" in song.tag:

                        song.tag.genre = Song['genre']
                    if "trackNumber" in Song:
                        song.tag.trackNumber = Song['trackNumber']
                    #song.tag.album = song['album']
                    #print song.tag.version
                    song.tag.save()
                    #print song.tag
                    #for song in self.activeSongList:

    def getAllPlaylists(self):
        """Gets all of the users playlists that they have created"""
        print("Getting all PLaylist contents")
        self.activePlayList = self.api.get_all_user_playlist_contents()
        baseDir = os.getcwd() + '/Playlists'
        for Playlist in self.activePlayList:
            playlistName = Playlist['name']
            print(playlistName)
            playlistSongs = Playlist['tracks']
            activeDir = baseDir + '/' + playlistName + '/'
            if not os.path.exists(activeDir):
                os.makedirs(activeDir)

            for Song in playlistSongs:
                #print Song
                if 'track' in Song:
                    activeSong = Song['track']
                    print('getting song : ' + activeSong['title'])
                    if not os.path.exists(activeDir + activeSong['title'] +
                                          'mp3'):
                        #it doesn't exists in the playlist yet, so we need to add it
                        songTitle = activeSong['title']
                        streamUrl = self.api.get_stream_url(
                            activeSong['storeId'])
                        urllib.request.urlretrieve(
                            streamUrl, activeDir + songTitle + '.mp3')
                        song = eyed3.load(activeDir + songTitle + '.mp3')
                        song.initTag()
                        song.tag.save()
                        song = eyed3.load(activeDir + songTitle + '.mp3')
                        if "title" in activeSong:
                            song.tag.title = activeSong['title']
                        if "artist" in activeSong:
                            song.tag.artist = activeSong['artist']
                        if "album" in activeSong:
                            song.tag.album = activeSong['album']
                        #song.tag.year = currentSong['year']
                        if "genre" in activeSong:
                            song.tag.genre = activeSong['genre']
                        if "trackNumber" in activeSong:
                            song.tag.trackNumber = activeSong['trackNumber']
                        #song.tag.album = song['album']
                        #print song.tag.version
                        song.tag.save()

    def getAllSongs(self):
        """Get all songs that the user has listen too that are associated with the account"""
        print("Getting all Songs")
        self.activeSongList = self.api.get_all_songs(False)
        #print str(self.activeSongList.count)
        baseDir = os.getcwd() + '/Music'
        print("Grabbed " + str(len(self.activeSongList)) + " Songs")
        #smallList = self.activeSongList[:3]
        for currentSong in self.activeSongList:
            #currentSong = self.activeSongList[0]
            #create a location sting for where the song will be saved.
            #check artist path
            artistPath = currentSong['artist'].strip().replace('/', '')
            albumPath = currentSong['album'].strip().replace('/', '')
            songTitle = currentSong['title'].replace('/', '')
            print(artistPath + " " + albumPath)
            songPath = '/' + artistPath + '/' + albumPath + "/"
            if os.path.exists(baseDir + songPath):
                print("Folder Structure exists")

            else:
                print("Folder Structure does not exist")

                if not os.path.exists(artistPath):
                    os.makedirs(baseDir + songPath)
                    print("created artitst and alartistPathbumn folder")
                else:
                    os.makedirs(baseDir + songPath)
            print(os.path.isfile(baseDir + songPath + songTitle + '.mp3'))
            print(baseDir + songPath + songTitle + '.mp3')
            if not os.path.isfile(baseDir + songPath + songTitle + '.mp3'):
                #get stream url
                streamUrl = self.api.get_stream_url(currentSong['id'])
                urllib.request.urlretrieve(
                    streamUrl, baseDir + songPath + songTitle + '.mp3')

                song = eyed3.load(baseDir + songPath + songTitle + '.mp3')
                song.initTag()
                song.tag.save()
                song = eyed3.load(baseDir + songPath + songTitle + '.mp3')
                song.tag.title = currentSong['title']
                song.tag.artist = currentSong['artist']
                song.tag.album = currentSong['album']
                #song.tag.year = currentSong['year']
                if 'genre' in currentSong:
                    song.tag.genre = currentSong['genre']
                song.tag.trackNumber = currentSong['trackNumber']
                #song.tag.album = song['album']
                #print song.tag.version
                song.tag.save()
                #print song.tag
                #for song in self.activeSongList:
            else:
                song = eyed3.load(baseDir + songPath + songTitle + '.mp3')
                song.initTag()
                song.tag.save()
                song = eyed3.load(baseDir + songPath + songTitle + '.mp3')
                song.tag.title = currentSong['title']
                song.tag.artist = currentSong['artist']

                song.tag.album = currentSong['album']
                if 'genre' in currentSong:
                    song.tag.genre = currentSong['genre']
                #song.tag.year = currentSong['year']
                song.tag.trackNumber = currentSong['trackNumber']
                #song.tag.album = song['album']
                #print song.tag.version
                song.tag.save()

    def Logout(self):
        self.api.logout()

    def updateSongs(self):
        """Update locally stored music with any newly listened to or added"""

    def getPlaylist(self, playlistName):
        """Gets a specific playlist by Name"""
        print("Getting " + playlistName)
Example #33
0
class MusicPlayer(object):

    def __init__(self):
        self.playlist = []                  # Array of all tracks
        self.playlist_id = 0                # Id of playlist
        self.current_track_index = 0        # Index of current song
        self.player = Player()              # MPlayer instance
        self.webclient = Webclient()        # Client for WebInterface
        self.mobileclient = Mobileclient()  # Client for MobileInterface
        self.timer = None                   # Timer to start next track
        self.deviceid = 0                   # DeviceId to use
        self.playtype = PlayType.LINEAR     # LINEAR or SHUFFLE

    def login(self, username, password):
        """ Login to Google Music.

        Keyword arguments:
        username -- the username
        password -- the password

        Returns:
        True if successful else False

        """

        # If either the web client or the mobile client failed to login return False
        if not self.webclient.login(username, password) or not self.mobileclient.login(username, password):
            return False

        # Use first found devices as ID
        devices = self.webclient.get_registered_devices();

        # Convert HEX to INT
        self.deviceid = int(devices[0]['id'], 16)

        return True

    def load_playlist(self, playlist_name):
        # Load playlist
        for playlist in self.mobileclient.get_all_user_playlist_contents():
            if playlist['name'] == playlist_name:
                for track_obj in playlist['tracks']:
                    track_obj['track']['id'] = track_obj['id']
                    self.playlist.append(track_obj['track'])

                # Set playlist_id
                self.playlist_id = playlist['id']
                break;

        # If playlist has not been found, create it
        if self.playlist_id == 0:
            self.playlist_id = self.mobileclient.create_playlist(playlist_name)

    def add_track_to_playlist(self, track):
        """ Append a track to the end of playlist

        Keyword arguments:
        track -- a dictionary containing the track informations

        """
        track_id = self.mobileclient.add_songs_to_playlist(self.playlist_id, track['nid'])[0]
        track['id'] = track_id
        self.playlist.append(track)

        # Notify all clients about the new track
        factory.forwarder.dispatch(PLAYLIST_EVENT_TRACK_ADDED, json.dumps(track))

    def remove_track_from_playlist(self, track_id):
        """ Removes a track from the playlist

        Keyword arguments:
        track_id -- The id of the track to remove

        """
        self.mobileclient.remove_entries_from_playlist(track_id)

        index_to_remove = self._find_index_of_track_id(track_id)

        del self.playlist[index_to_remove]

        factory.forwarder.dispatch(PLAYLIST_EVENT_TRACK_REMOVED, track_id)

    def play_track(self, track_id):
        """ Play a track

        Keyword arguments:
        track_id -- Id of the track to play

        """

        index_of_track = self._find_index_of_track_id(track_id)

        track_to_play = self.playlist[index_of_track]

        if track_to_play is not None:
            # Request stream url from google music
            stream_url = self.mobileclient.get_stream_url(track_to_play["storeId"], self.deviceid)

            # Load stream url to mplayer
            self.player.loadfile(stream_url)

            # For some reason OSX needs to unpause mplayer
            if sys.platform == "darwin":
                self.player.pause()

            # Set track
            self.current_track_index = index_of_track

            # Cancel previous timer
            if self.timer is not None:
                self.timer.cancel()

            # How many minutes does the track last
            track_duration = long(track_to_play["durationMillis"]) / 1000

            # Set Timer to play next track when trackDuration is over
            self.timer = Timer(track_duration, self.play_next_track)
            self.timer.daemon = True
            self.timer.start()

            print "playing", track_to_play["artist"], " - ", track_to_play["title"], " : ", stream_url

            # Fire event that a new track is playing
            factory.forwarder.dispatch(TRACK_EVENT_PLAYBACK, json.dumps(track_to_play))

            return True
        else:
            return False

    def play_next_track(self):
        """ Play the next track in the playlist.

        Returns:
        True or False

        """

        if self.playtype == PlayType.LINEAR:
            # Index of next track to play
            next_track_index = self.current_track_index + 1

            # Restart at index 0 if end of playlist is reached
            if next_track_index >= len(self.playlist):
                next_track_index = 0

        elif self.playtype == PlayType.SHUFFLE:
            # Index of next track to play at random
            next_track_index = random.randrange(0, len(self.playlist), 1)

        # Obtain the id of the next track to play
        next_track_id = self.playlist[next_track_index]['id']

        # Play track with that id
        return self.play_track(next_track_id)

    def play_previous_track(self):
        """ Play the previous track in the playlist.

        Returns:
        True or False

        """

        if self.playtype == PlayType.LINEAR:
            # Index of previous track to play
            previous_track_index = self.current_track_index - 1

            # Contiune from the end of the playlist
            if previous_track_index <= 0:
                previous_track_index = len(self.playlist) - 1

        elif self.playtype == PlayType.SHUFFLE:
            # Index of the previous track is random
            previous_track_index = random.randrange(0, len(self.playlist), 1)

        # Obtain the id of the previous track to play
        previous_track_id = self.playlist[previous_track_index]['id']

        # Play track with that id
        return self.play_track(previous_track_id)

    def stop(self):
        """ Stop playback.

        """

        if self.timer is not None:
            self.timer.cancel()

        if self.player is not None:
            self.player.stop()

    def play(self):
        """ Start playing current track

        Returns:
        True if track has been started. Else False

        """
        current_track_id = self.playlist[self.current_track_index]
        return self.play_track(current_track_id)

    def _find_index_of_track_id(self, track_id):
        index = 0

        for track in self.playlist:
            if track['id'] == track_id:
                return index
            index += 1

        return None
class PlaylistManager(object):
    def __init__(self, email_file):
        self.login(email_file)
        self._get_songs()
        self._get_playlists()

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

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

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

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


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

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

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


    def make_local_xspf_playlists(self):
        """write playlists in xspf format to named files in the playlist folder.
        Credit to https://github.com/alastair/xspf for the wonderful xspf python module"""
        # output_path = 'playlists'
        output_path = os.path.expanduser('~/Music/Playlists')
        music_path = os.path.expanduser('~/Music')
        extension = '.mp3'
        if not os.path.exists(output_path):
            os.mkdir(output_path)
        if not os.path.isdir(output_path):
            raise Exception("Not a folder!")
        for playlist in self.all_playlists:
            output_file_path = os.path.join(output_path, playlist[u'name'] + '.xspf')
            playlist_xspf = xspf.Xspf()
            playlist_xspf.title = playlist[u'name']
            for track in playlist['tracks']:
                track_xspf = xspf.Track()
                track_id = track['trackId']
                track_dict = self.all_songs_by_id[track_id]
                artist = track_dict['artist']
                album = track_dict['album']
                title = track_dict['title']
                track_xspf.title = title
                track_xspf.album = album
                track_xspf.creator = artist
                # replace is to fix the slash in 'AC/DC'
                track_path = 'file://' + os.path.join(music_path, artist.replace('/', ','), album.replace('/', ','),
                                                      title.replace('/', ',') + extension)
                track_xspf.location = track_path
                length = str(int(track_dict['durationMillis']) / 1000)
                track_xspf.duration = length
                playlist_xspf.add_track(track_xspf)
            with open(output_file_path, 'wb') as output_file:
                # we write this header so that Sublime Text recognizes it as an XML file, and sets syntax accordingly
                output_file.write('<?xml version="1.0" encoding="UTF-8"?>\n')
                output_file.write(playlist_xspf.toXml(pretty_print=True))
Example #35
0
class GMusic(object):
    def __init__(self):
        self.authenticated = False
        self.all_access = False
        self.library_loaded = False
        self.all_songs = []
        self.letters = {}
        self.artists = {}
        self.albums = {}
        self.genres = {}
        self.tracks_by_letter = {}
        self.tracks_by_artist = {}
        self.tracks_by_album = {}
        self.tracks_by_genre = {}
        self._device = None
        self._webclient = Webclient(debug_logging=False)
        self._mobileclient = Mobileclient(debug_logging=False)
        self._playlists = []
        self._playlist_contents = []
        self._stations = []

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

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

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

        else:
            return self.all_songs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.library_loaded = True

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

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

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

        return self._playlists

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

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

        return tracks

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

        return playlist

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

        return self._stations

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

        return tracks

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

        return genres

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

        return station

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

        return results

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

        return results

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

        return results

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

        return track

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

        return tracks

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

        return stream_url

    def reset_library(self):
        self.library_loaded = False
        self.all_songs[:] = []
        self._playlists[:] = []
        self._playlist_contents[:] = []
        self._stations[:] = []
        self.letters.clear()
        self.artists.clear()
        self.albums.clear()
        self.genres.clear()
        self.tracks_by_letter.clear()
        self.tracks_by_artist.clear()
        self.tracks_by_album.clear()
        self.tracks_by_genre.clear()
Example #36
0
from gmusicapi import Mobileclient
import json

client = Mobileclient()

oauth = raw_input("first time?")
if oauth == "y":
    client.perform_oauth()
else:
    # todo: store oauth data in non-default
    client.oauth_login(Mobileclient.FROM_MAC_ADDRESS)

playlists = client.get_all_user_playlist_contents()
songs = []

# todo: allow playlist selection
playlist = "Main"

for p in playlists:
    if p["name"] == playlist:
        for track in p["tracks"]:
            try:
                song = client.get_track_info(track["trackId"])
                song["entryId"] = track["id"]
                songs.append(song)
            except:
                print("Wasn't able to obtain information for one song")

# with open("data.txt", "w") as outfile:
#     json.dump(songs, outfile)
Example #37
0
class GMusicClient(ContentConsumer):
    """Element in charge of interfacing with GMusicApi Client"""

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

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

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

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

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

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

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

    def scrape_song(self, track):
        return track

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def format_subitems(self, t, args):
        return (t[args["name"]], t[args["id"]], args["command"], t[args["alt"]])
Example #38
0
class GmusicStore(BackendStore):

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

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

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

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

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

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

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

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

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

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

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

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

        self.api = Mobileclient()

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

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

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

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

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

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

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

        return self.tracks.get(id, None)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return tracklist

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

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

                playlist_container.tracks = self.parse_playlist_tracks(playlist_tracks)

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

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

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

        self.info("Finished parsing")

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

    def queue_update(self, error_or_failure):
        # We use the reactor to queue another updating of our data
        print error_or_failure
        reactor.callLater(self.refresh, self.update_loop)
if migration_type != 'stations':
    log.info('Retrieving tracks from ' + export_username)
    export_tracks = export_api.get_all_songs()
    # strip out any tracks that are not available on All Access
    all_tracks = [t for t in export_tracks if track_has_aa_data(t) and not t.get('deleted')]

if migration_type == 'all' or migration_type == 'ratings':
    log.info('Retrieving thumbs up tracks from ' + export_username)
    export_thumbs_up = export_api.get_thumbs_up_songs()
    # strip out any tracks that are not available on All Access
    thumbs_up_tracks = [t for t in export_thumbs_up if track_has_aa_data(t)]

if migration_type == 'all' or migration_type == 'playlists':
    log.info('Retrieving playlists from ' + export_username)
    export_playlists = export_api.get_all_user_playlist_contents()
    playlists = [p for p in export_playlists if not p.get('deleted')]

if migration_type == 'all' or migration_type == 'stations':  
    log.info('Retrieving stations from ' + export_username)
    export_stations = export_api.get_all_stations()
    radio_stations = [s for s in export_stations if not s.get('deleted')]

log.info('Export complete')
export_api.logout()
log.debug('API logout for ' + export_username)

# import tracks
if migration_type == 'all' or migration_type == 'tracks':
    log.info('Importing ' + str(len(all_tracks)) + ' All Access tracks to ' + import_username)
    
Example #40
0
class GMusicWrapper(object):
    def __init__(self, username, password, logger=None):
        self._api = Mobileclient()
        self.logger = logger
        success = self._api.login(
            username, password,
            environ.get('ANDROID_ID', Mobileclient.FROM_MAC_ADDRESS))

        if not success:
            raise Exception("Unsuccessful login. Aborting!")

        # Populate our library
        self.library = {}
        self.indexing_thread = threading.Thread(target=self.index_library)
        self.indexing_thread.start()

    def populate_library(
        self
    ):  #TODO: Use this as a function to refresh the library with Alexa via voice commands.
        # Populate our library
        self.library = {}
        self.indexing_thread = threading.Thread(target=self.index_library)
        self.indexing_thread.start()

    def _search(self, query_type, query):
        try:
            results = self._api.search(query)
        except CallFailure:
            return []

        hits_key = "%s_hits" % query_type

        if hits_key not in results:
            return []

        # Ugh, Google had to make this schema nonstandard...
        if query_type == 'song':
            query_type = 'track'

        return [x[query_type] for x in results[hits_key]]

    def _search_library_for_first(self, query_type, query):
        #try searching the library instead of the api
        for trackid, trackdata in self.library.items():
            if query_type in trackdata:
                if query.lower() in trackdata[query_type].lower():
                    return trackdata
        return None

    def _search_library(self, query_type, query):
        #try searching the library instead of the api
        found = []
        for trackid, trackdata in self.library.items():
            if query_type in trackdata:
                if query.lower() in trackdata[query_type].lower():
                    found.append(trackdata)
        if not found:
            return None
        return found

    def is_indexing(self):
        return self.indexing_thread.is_alive()

    def index_library(self):
        """
        Downloads the a list of every track in a user's library and populates
        self.library with storeIds -> track definitions
        """
        self.logger.debug('Fetching library...')
        tracks = self.get_all_songs()

        for track in tracks:
            song_id = track['id']
            self.library[song_id] = track

        self.logger.debug('Done! Discovered %d tracks.' % len(self.library))

    def get_artist(self, name):
        """
        Fetches information about an artist given its name
        """
        search = self._search("artist", name)

        if len(search) == 0:
            search_lib = self._search_library("artist", name)
            if search_lib is not None:
                self.logger.debug(search_lib)
                return search_lib
            return False

        return self._api.get_artist_info(search[0]['artistId'],
                                         max_top_tracks=100)

    def get_album(self, name, artist_name=None):
        if artist_name:
            name = "%s %s" % (name, artist_name)

        search = self._search("album", name)

        if len(search) == 0:
            search_lib = self._search_library("album", name)
            if search_lib is not None:
                self.logger.debug(search_lib)
                return search_lib
            return False

        return self._api.get_album_info(search[0]['albumId'])

    def get_latest_album(self, artist_name=None):
        search = self._search("artist", artist_name)

        if len(search) == 0:
            return False

        artist_info = self._api.get_artist_info(search[0]['artistId'],
                                                include_albums=True)
        album_info = artist_info['albums']
        sorted_list = sorted(album_info.__iter__(),
                             key=lambda s: s['year'],
                             reverse=True)

        for index, val in enumerate(sorted_list):
            album_info = self._api.get_album_info(
                album_id=sorted_list[index]['albumId'], include_tracks=True)
            if len(album_info['tracks']) >= 5:
                return album_info

        return False

    def get_album_by_artist(self, artist_name, album_id=None):
        search = self._search("artist", artist_name)
        if len(search) == 0:
            return False

        artist_info = self._api.get_artist_info(search[0]['artistId'],
                                                include_albums=True)
        album_info = artist_info['albums']
        random.shuffle(album_info)

        for index, val in enumerate(album_info):
            album = self._api.get_album_info(
                album_id=album_info[index]['albumId'], include_tracks=True)
            if album['albumId'] != album_id and len(album['tracks']) >= 5:
                return album

        return False

    def get_song(self, song_name, artist_name=None, album_name=None):
        if artist_name:
            name = "%s %s" % (artist_name, song_name)
        elif album_name:
            name = "%s %s" % (album_name, song_name)

        self.logger.debug("get_song() : name: %s" % (name))

        search = self._search("song", name)

        self.logger.debug("result length: %d" % len(search))

        if len(search) == 0:
            search_lib = self._search_library_for_first("title", name)
            if search_lib is not None:
                return search_lib
            return False

        if album_name:
            for i in range(0, len(search) - 1):
                if album_name in search[i]['album']:
                    return search[i]
        return search[0]

    def get_station(self, title, track_id=None, artist_id=None, album_id=None):
        if artist_id is not None:
            if album_id is not None:
                if track_id is not None:
                    return self._api.create_station(title, track_id=track_id)
                return self._api.create_station(title, album_id=album_id)
            return self._api.create_station(title, artist_id=artist_id)

    def get_station_tracks(self, station_id):
        return self._api.get_station_tracks(station_id)

    def get_google_stream_url(self, song_id):
        return self._api.get_stream_url(song_id)

    def get_stream_url(self, song_id):
        return "%s/alexa/stream/%s" % (environ['APP_URL'], song_id)

    def get_thumbnail(self, artist_art):
        # return artist_art.replace("http://", "https://") //OLD
        artistArtKey = 'artistArtRef'
        albumArtKey = 'albumArtRef'
        if artist_art is None:
            return self.default_thumbnail()
        elif artistArtKey in artist_art:
            artist_art = artist_art[artistArtKey]
        elif albumArtKey in artist_art:
            artist_art = artist_art[albumArtKey]
        else:
            return self.default_thumbnail()
        if type(artist_art) is list:
            if type(artist_art[0]) is dict:
                artUrl = artist_art[0]['url']
        elif type(artist_art) is dict:
            artUrl = artist_art['url']
        else:
            artUrl = artist_art
        return self.urlReplaceWithSecureHttps(artUrl)

    def urlReplaceWithSecureHttps(self, url):
        return url.replace("http://", "https://")

    def default_thumbnail(self):
        return 'https://lh3.googleusercontent.com/gdBHEk-u3YRDtuCU3iDTQ52nZd1t4GPmldYaT26Jh6EhXgp1mlhQiuLFl4eXDAXzDig5'

    def get_all_user_playlist_contents(self):
        return self._api.get_all_user_playlist_contents()

    def get_all_songs(self):
        return self._api.get_all_songs()

    def rate_song(self, song, rating):
        return self._api.rate_songs(song, rating)

    def extract_track_info(self, track):
        # When coming from a playlist, track info is nested under the "track"
        # key
        if 'track' in track:
            track = track['track']

        if 'trackId' in track:
            return (self.library[track['trackId']], track['trackId'])

        if self.use_library_first():
            #Using free version track id first
            if 'id' in track:
                return (track, track['id'])
        if 'storeId' in track:
            return track, track['storeId']
        return (None, None)

    def get_artist_album_list(self, artist_name):
        search = self._search("artist", artist_name)
        if len(search) == 0:
            return "Unable to find the artist you requested."

        artist_info = self._api.get_artist_info(search[0]['artistId'],
                                                include_albums=True)
        album_list_text = "Here's the album listing for %s: " % artist_name

        counter = 0
        for index, val in enumerate(artist_info['albums']):
            if counter > 25:  # alexa will time out after 10 seconds if the list takes too long to iterate through
                break
            album_info = self._api.get_album_info(
                album_id=artist_info['albums'][index]['albumId'],
                include_tracks=True)
            if len(album_info['tracks']) > 5:
                counter += 1
                album_list_text += (
                    artist_info['albums'][index]['name']) + ", "
        return album_list_text

    def get_latest_artist_albums(self, artist_name):
        search = self._search("artist", artist_name)

        if len(search) == 0:
            return False

        artist_info = self._api.get_artist_info(search[0]['artistId'],
                                                include_albums=True)
        album_list = artist_info['albums']

        sorted_list = sorted(album_list.__iter__(),
                             key=lambda s: s['year'],
                             reverse=True)

        speech_text = 'The latest albums by %s are ' % artist_name

        counter = 0
        for index, val in enumerate(sorted_list):
            if counter > 5:
                break
            else:
                album_info = self._api.get_album_info(
                    album_id=sorted_list[index]['albumId'],
                    include_tracks=True)
                if len(album_info['tracks']) >= 5:
                    counter += 1
                    album_name = sorted_list[index]['name']
                    album_year = sorted_list[index]['year']
                    speech_text += '%s, released in %d, ' % (album_name,
                                                             album_year)

        return speech_text

    def closest_match(self,
                      request_name,
                      all_matches,
                      artist_name='',
                      minimum_score=70):
        # Give each match a score based on its similarity to the requested
        # name
        self.logger.debug("The artist name is " + str(artist_name))
        request_name = request_name.lower() + artist_name.lower()
        scored_matches = []
        for i, match in enumerate(all_matches):
            try:
                name = match['name'].lower()
            except (KeyError, TypeError):
                i = match
                name = all_matches[match]['title'].lower()
                if artist_name != "":
                    name += all_matches[match]['artist'].lower()

            scored_matches.append({
                'index': i,
                'name': name,
                'score': fuzz.ratio(name, request_name)
            })

        sorted_matches = sorted(scored_matches,
                                key=lambda a: a['score'],
                                reverse=True)
        top_scoring = sorted_matches[0]
        self.logger.debug("The top scoring match was: " + str(top_scoring))
        best_match = all_matches[top_scoring['index']]

        # Make sure the score is at least the min score value
        if top_scoring['score'] < minimum_score:
            return None

        return best_match

    def get_genres(self, parent_genre_id=None):
        return self._api.get_genres(parent_genre_id)

    def increment_song_playcount(self, song_id, plays=1, playtime=None):
        return self._api.increment_song_playcount(song_id, plays, playtime)

    def get_song_data(self, song_id):
        return self._api.get_track_info(song_id)

    def use_library_first(self):
        return environ['USE_LIBRARY_FIRST'].lower() == 'true'

    @classmethod
    def generate_api(cls, **kwargs):
        return cls(environ['GOOGLE_EMAIL'], environ['GOOGLE_PASSWORD'],
                   **kwargs)
Example #41
0
class GPMClient(object):
    """
    Google Play Music client.
    """

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

    #------------------------------------------------------------------------------

    def __init__(self, email, password, device_id):
        self.__api = Mobileclient()
        self.logged_in = False
        self.__device_id = device_id

        attempts = 0
        while not self.logged_in and attempts < 3:
            self.logged_in = self.__api.login(email, password, device_id)
            attempts += 1

        self.all_tracks = dict()
        self.playlists = dict()
        self.library = dict()

#------------------------------------------------------------------------------

    def logout(self):
        self.__api.logout()

#------------------------------------------------------------------------------

    def update_local_lib(self):
        songs = self.__api.get_all_songs()
        self.playlists[self.thumbs_up_playlist_name] = list()

        # Get main library
        song_map = dict()
        for song in songs:
            if "rating" in song and song["rating"] == "5":
                self.playlists[self.thumbs_up_playlist_name].append(song)

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

            song_map[song_id] = song

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

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

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

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

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

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

        # Get all playlists
        plists = self.__api.get_all_user_playlist_contents()
        for plist in plists:
            plist_name = plist["name"]
            self.playlists[plist_name] = list()
            for track in plist["tracks"]:
                if track["trackId"] not in song_map:
                    song = song_map[track["trackId"]] = track["track"]
                    song["id"] = track["trackId"]
                else:
                    song = song_map[track["trackId"]]
                self.playlists[plist_name].append(song)

#------------------------------------------------------------------------------

    def get_stream_url(self, song):
        return self.__api.get_stream_url(song["id"], self.__device_id)

#------------------------------------------------------------------------------

    def rate_song(self, song, rating):
        try:
            song["rating"] = rating
            song_list = [song]
            self.__api.change_song_metadata(song_list)
            print "Gave a Thumbs Up to {0} by {1} on Google Play.".format(
                song["title"].encode("utf-8"), song["artist"].encode("utf-8"))
        except RuntimeError:
            print "Error giving a Thumbs Up on Google Play."
Example #42
0
    def handle(self, *args, **options):
        if GPLAY_PASS == "" or GPLAY_USER == "":
            self.stdout.write('Credentials not set up. Please edit settings.py')
            return

        api = Mobileclient()
        if not api.login(GPLAY_USER,GPLAY_PASS):
            self.stdout.write('Incorrect credentials, login failed')
            return

        self.stdout.write('Connected to Google Music, downloading data...')
        #library = []
        library = api.get_all_songs()
        self.stdout.write('Data downloaded!')
        self.stdout.write('Clearing DB...')
        cursor = connection.cursor()
        # This can take a long time using ORM commands on the Pi, so lets Truncate
        cursor.execute('DELETE FROM ' + Track._meta.db_table)
        cursor.execute('DELETE FROM ' + Album._meta.db_table)
        cursor.execute('DELETE FROM ' + Artist._meta.db_table)
        cursor.execute('DELETE FROM ' + Playlist._meta.db_table)
        cursor.execute('DELETE FROM ' + PlaylistConnection._meta.db_table)
        self.stdout.write('Parsing new data...')

        # Easier to keep track of who we've seen like this...
        artists = []
        albums = []
        count = len(library)
        self.stdout.write(str(count) + ' tracks found')
        i = 0
        for song in library:
            i = i + 1
            track = Track()

            if song['albumArtist'] == "":
                if song['artist'] == "":
                    a = "Unknown Artist"
                else:
                    a = song['artist']
            else:
                a = song['albumArtist']

            if a not in artists:
                artist = Artist()
                artist.name = a

                try:
                    artist.art_url = song['artistArtRef'][0]['url']
                except:
                    print "No Art found."
                artist.save()
                artists.append(a)
                self.stdout.write('Added artist: ' + a)
                self.stdout.write(str(i) + '/' + str(count) + ' tracks completed')
            else:
                artist = Artist.objects.get(name=a)
            track.artist = artist

            if song['album'] + a not in albums:
                album = Album()
                album.name = song['album']
                album.artist = artist
                try:
                    album.year = song['year']
                except:
                    pass

                try:
                    album.art_url = song['albumArtRef'][0]['url']
                except:
                    print "No Art found."
                album.save()
                albums.append(song['album'] + a)
            else:
                album = Album.objects.get(name=song['album'], artist=artist)
            track.album = album

            track.name = song['title']
            track.stream_id = song['id']
            try:
                track.track_no = song['trackNumber']
            except:
                track.track_no = 0
            track.save()

        self.stdout.write('All tracks saved!')
        self.stdout.write('Getting Playlists...')
        
        self.stdout.write('Saving playlists...')

        self.stdout.write('Getting playlist contents.')
        playlists = api.get_all_user_playlist_contents()
        for playlist in playlists:
            p = Playlist()
            p.pid = playlist['id']
            p.name = playlist['name']
            p.save()
            for entry in playlist['tracks']:
                try:
                    track = Track.objects.get(stream_id=entry['trackId'])
                    pc = PlaylistConnection()
                    pc.playlist = p
                    pc.track = track
                    pc.save()
                except Exception:
                    print "Not found."

        self.stdout.write('Library saved!')
Example #43
0
from gmusicapi import Mobileclient
import json

api = Mobileclient()

email = ""
password = ""

logged_in = api.login('', '', Mobileclient.FROM_MAC_ADDRESS)

if logged_in:
  library = api.get_all_user_playlist_contents()
	
	f = open("textData/googleData.txt", "w+")
	f.write(json.dumps(library))
	f.close()

  print("Success")
else:
  print "Fail"
Example #44
0

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


if __name__ == '__main__':
    print('Logging in...')
    if do_login():
        tracks = client.get_all_songs()
        ids = [
            '648b82b7-015f-3823-bf21-3d31d7dc822d',
            '02a9b776-8606-37bd-8a92-c723e558c8d5'
        ]
        pEntry = None
        for track in tracks:
            if track['id'] in ids or track.get(
                    'storeId', None) in ids or track.get('nid', None) in ids:
                pEntry = track
        if pEntry is not None:
            print('Playlist Entry')
            print(dumps(pEntry, indent=4))
        print(dumps(tracks[0], indent=4))
        print(dumps(client.get_all_playlists()[0], indent=4))
        print(dumps(client.get_all_user_playlist_contents()[0], indent=4))
Example #45
0
class GMusic(object):
    def __init__(self):
        self.authenticated = False
        self.all_access = False
        self._device = None
        self._webclient = Webclient(debug_logging=False)
        self._mobileclient = Mobileclient(debug_logging=False)
        self._playlists = []
        self._playlist_contents = []
        self._all_songs = []
        self._all_artists = {}
        self._all_albums = {}
        self._all_genres = {}
        self._stations = []

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

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

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

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

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

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

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

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

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

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

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

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

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

        return self._all_artists

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

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

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

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

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

        return self._all_albums

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

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

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

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

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

        return self._all_genres

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

        return self._playlists

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

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

        return tracks

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

        return playlist

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

        return self._stations

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

        return tracks

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

        return genres

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

        return station

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

        return results

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

        return results

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

        return results

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

        return stream_url
Example #46
0
class tizgmusicproxy(object):
    """A class for logging into a Google Play Music account and retrieving song
    URLs.

    """

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

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

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

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

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

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

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

        """
        self.__gmusic.logout()

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        Requires Unlimited subscription.

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

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

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

        Requires Unlimited subscription.

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

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

            if not len(self.queue):
                raise KeyError

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

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

        Requires Unlimited subscription.

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

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

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

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

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

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

        Requires Unlimited subscription.

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

        try:

            self.__enqueue_situation_unlimited(arg)

            if not len(self.queue):
                raise KeyError

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

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

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

        Requires Unlimited subscription.

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

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

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

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

        Requires Unlimited subscription.

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

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

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

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

        Requires Unlimited subscription.

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            self.song_map[song_id] = song

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

            if not situation:
                raise KeyError

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

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

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

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

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

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

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

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

        """

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

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

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

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

        return result
Example #47
0
    export_tracks = export_api.get_all_songs()
    # strip out any tracks that are not available on All Access
    all_tracks = [
        t for t in export_tracks
        if track_has_aa_data(t) and not t.get('deleted')
    ]

if migration_type == 'all' or migration_type == 'ratings':
    log.info('Retrieving thumbs up/promoted tracks from ' + export_username)
    export_thumbs_up = export_api.get_promoted_songs()
    # strip out any tracks that are not available on All Access
    thumbs_up_tracks = [t for t in export_thumbs_up if track_has_aa_data(t)]

if migration_type == 'all' or migration_type == 'playlists':
    log.info('Retrieving playlists from ' + export_username)
    export_playlists = export_api.get_all_user_playlist_contents()
    playlists = [p for p in export_playlists if not p.get('deleted')]

if migration_type == 'all' or migration_type == 'stations':
    log.info('Retrieving stations from ' + export_username)
    export_stations = export_api.get_all_stations()
    radio_stations = [s for s in export_stations if not s.get('deleted')]

log.info('Export complete')
export_api.logout()
log.debug('API logout for ' + export_username)

# import tracks
if migration_type == 'all' or migration_type == 'tracks':
    log.info('Importing ' + str(len(all_tracks)) + ' All Access tracks to ' +
             import_username)
class tizgmusicproxy(object):
    """A class for accessing a Google Music account to retrieve song URLs.
    """

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

    def __init__(self, email, password, device_id):
        self.__api = Mobileclient()
        self.logged_in = False
        self.__device_id = device_id
        self.queue = list()
        self.queue_index = -1
        self.play_mode = 0
        self.now_playing_song = None

        attempts = 0
        while not self.logged_in and attempts < 3:
            self.logged_in = self.__api.login(email, password)
            attempts += 1

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

    def logout(self):
        self.__api.logout()

    def update_local_lib(self):
        songs = self.__api.get_all_songs()
        self.playlists[self.thumbs_up_playlist_name] = list()

        # Get main library
        song_map = dict()
        for song in songs:
            if "rating" in song and song["rating"] == "5":
                self.playlists[self.thumbs_up_playlist_name].append(song)

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

            song_map[song_id] = song

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

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

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

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

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

        # Sort albums by track number
        for artist in self.library.keys():
            logging.info("Artist : {0}".format(artist.encode("utf-8")))
            for album in self.library[artist].keys():
                logging.info("   Album : {0}".format(album.encode("utf-8")))
                if album == self.all_songs_album_title:
                    sorted_album = sorted(self.library[artist][album], key=lambda k: k["title"])
                else:
                    sorted_album = sorted(self.library[artist][album], key=lambda k: k.get("trackNumber", 0))
                self.library[artist][album] = sorted_album

        # Get all playlists
        plists = self.__api.get_all_user_playlist_contents()
        for plist in plists:
            plist_name = plist["name"]
            self.playlists[plist_name] = list()
            for track in plist["tracks"]:
                try:
                    song = song_map[track["trackId"]]
                    self.playlists[plist_name].append(song)
                except IndexError:
                    pass

    def current_song_title_and_artist(self):
        logging.info("current_song_title_and_artist")
        song = self.now_playing_song
        if song is not None:
            title = self.now_playing_song["title"]
            artist = self.now_playing_song["artist"]
            logging.info("Now playing {0} by {1}".format(title.encode("utf-8"), artist.encode("utf-8")))
            return artist.encode("utf-8"), title.encode("utf-8")
        else:
            return "", ""

    def current_song_album_and_duration(self):
        logging.info("current_song_album_and_duration")
        song = self.now_playing_song
        if song is not None:
            album = self.now_playing_song["album"]
            duration = self.now_playing_song["durationMillis"]
            logging.info("album {0} duration {1}".format(album.encode("utf-8"), duration.encode("utf-8")))
            return album.encode("utf-8"), int(duration)
        else:
            return "", 0

    def current_song_track_number_and_total_tracks(self):
        logging.info("current_song_track_number_and_total_tracks")
        song = self.now_playing_song
        if song is not None:
            track = self.now_playing_song["trackNumber"]
            total = self.now_playing_song["totalTrackCount"]
            logging.info("track number {0} total tracks {1}".format(track, total))
            return track, total
        else:
            logging.info("current_song_track_number_and_total_tracks : not found")
            return 0, 0

    def clear_queue(self):
        self.queue = list()
        self.queue_index = -1

    def enqueue_artist(self, arg):
        try:
            artist = self.library[arg]
            count = 0
            for album in artist:
                for song in artist[album]:
                    self.queue.append(song)
                    count += 1
            logging.info("Added {0} tracks by {1} to queue".format(count, arg))
        except KeyError:
            logging.info("Cannot find {0}".format(arg))
            raise

    def enqueue_album(self, arg):
        try:
            for artist in self.library:
                for album in self.library[artist]:
                    logging.info("enqueue album : {0} | {1}".format(artist.encode("utf-8"), album.encode("utf-8")))
                    if album.lower() == arg.lower():
                        count = 0
                        for song in self.library[artist][album]:
                            self.queue.append(song)
                            count += 1
                        logging.info(
                            "Added {0} tracks from {1} by "
                            "{2} to queue".format(count, album.encode("utf-8"), artist.encode("utf-8"))
                        )
        except KeyError:
            logging.info("Cannot find {0}".format(arg))
            raise

    def enqueue_playlist(self, arg):
        try:
            playlist = self.playlists[arg]
            count = 0
            for song in playlist:
                self.queue.append(song)
                count += 1
            logging.info("Added {0} tracks from {1} to queue".format(count, arg))
        except KeyError:
            logging.info("Cannot find {0}".format(arg))
            raise

    def next_url(self):
        logging.info("next_url")
        if len(self.queue):
            self.queue_index += 1
            if (self.queue_index < len(self.queue)) and (self.queue_index >= 0):
                next_song = self.queue[self.queue_index]
                return self.__get_song_url(next_song)
            else:
                self.queue_index = -1
                return self.next_url()
        else:
            return ""

    def prev_url(self):
        if len(self.queue):
            self.queue_index -= 1
            if (self.queue_index < len(self.queue)) and (self.queue_index >= 0):
                prev_song = self.queue[self.queue_index]
                return self.__get_song_url(prev_song)
            else:
                self.queue_index = len(self.queue)
                return self.prev_url()
        else:
            return ""

    def __get_song_url(self, song):
        song_url = self.__api.get_stream_url(song["id"], self.__device_id)
        try:
            self.now_playing_song = song
            return song_url
        except AttributeError:
            logging.info("Could not retrieve song url!")
            raise
Example #49
0
    sys.exit()

clientId = "<<YOUR SPOTIFY APP CLIENT ID>>"
clientSecret = "<<YOUR SPOTIFY APP CLIENT SECRET>>"

spotify = spotipy.Spotify(client_credentials_manager=SpotifyClientCredentials(
    client_id=clientId, client_secret=clientSecret))

mc = Mobileclient()
if os.path.exists(googleEmail) == False:
    mc.perform_oauth(storage_filepath=googleEmail, open_browser=True)
mc.oauth_login(Mobileclient.FROM_MAC_ADDRESS, oauth_credentials=googleEmail)

playlists = {}

for playlist in mc.get_all_user_playlist_contents():
    print(playlist['name'])
    playlists[playlist['name']] = []
    currentPlaylist = playlists[playlist['name']]
    for entry in playlist['tracks']:
        if 'track' in entry:
            title = entry['track']['title']
            artist = entry['track']['artist']
            album = entry['track']['album']

            spotifyResults = spotify.search(q=artist + ', ' + title,
                                            type='track')
            if len(spotifyResults['tracks']['items']) > 0:
                spotifyTrackUri = spotifyResults['tracks']['items'][0]['uri']
            else:
                spotifyTrackUri = "unknown"
Example #50
0
args = parser.parse_args()

password = ''
if 'GOOGLE_PLAY_PASSWORD' in os.environ:
    password = os.environ['GOOGLE_PLAY_PASSWORD']
if not password:
    password = getpass.getpass('Google Play account password: '******'tracks'] for playlist in googlePlayPlaylists
                               if playlist['name'] == args.playlist][0]

googlePlayTrackIds = list(map(lambda x: x['trackId'], googlePlayPlaylistTrackList))

print("Fetching list of files in Google Play library")
allTracksInGooglePlayLibrary = api.get_all_songs()

print("Fetching track info for tracks in playlist")
tracksInGooglePlayPlaylist = [track for track in allTracksInGooglePlayLibrary
                              if track['id'] in googlePlayTrackIds]

googlePlayTrackInfos = \
Example #51
0
    def handle(self, *args, **options):
        if GPLAY_PASS == "" or GPLAY_USER == "":
            self.stdout.write(
                'Credentials not set up. Please edit settings.py')
            return

        api = Mobileclient()
        if not api.login(GPLAY_USER, GPLAY_PASS, DEVICE_ID):
            self.stdout.write('Incorrect credentials, login failed')
            return

        self.stdout.write('Connected to Google Music, downloading data...')
        #library = []
        library = api.get_all_songs()
        self.stdout.write('Data downloaded!')
        self.stdout.write('Clearing DB...')
        cursor = connection.cursor()
        # This can take a long time using ORM commands on the Pi, so lets Truncate
        cursor.execute('DELETE FROM ' + Track._meta.db_table)
        cursor.execute('DELETE FROM ' + Album._meta.db_table)
        cursor.execute('DELETE FROM ' + Artist._meta.db_table)
        cursor.execute('DELETE FROM ' + Playlist._meta.db_table)
        cursor.execute('DELETE FROM ' + PlaylistConnection._meta.db_table)
        self.stdout.write('Parsing new data...')

        # Easier to keep track of who we've seen like this...
        artists = []
        albums = []
        count = len(library)
        self.stdout.write(str(count) + ' tracks found')
        i = 0
        for song in library:
            i = i + 1
            track = Track()

            if song['albumArtist'] == "":
                if song['artist'] == "":
                    a = "Unknown Artist"
                else:
                    a = song['artist']
            else:
                a = song['albumArtist']

            if a not in artists:
                artist = Artist()
                artist.name = a

                try:
                    artist.art_url = song['artistArtRef'][0]['url']
                except:
                    print "No Art found."
                artist.save()
                artists.append(a)
                self.stdout.write('Added artist: ' + a)
                self.stdout.write(
                    str(i) + '/' + str(count) + ' tracks completed')
            else:
                artist = Artist.objects.get(name=a)
            track.artist = artist

            if song['album'] + a not in albums:
                album = Album()
                album.name = song['album']
                album.artist = artist
                try:
                    album.year = song['year']
                except:
                    pass

                try:
                    album.art_url = song['albumArtRef'][0]['url']
                except:
                    print "No Art found."
                album.save()
                albums.append(song['album'] + a)
            else:
                album = Album.objects.get(name=song['album'], artist=artist)
            track.album = album

            track.name = song['title']
            track.stream_id = song['id']
            try:
                track.track_no = song['trackNumber']
            except:
                track.track_no = 0
            track.save()

        self.stdout.write('All tracks saved!')
        self.stdout.write('Getting Playlists...')

        self.stdout.write('Saving playlists...')

        self.stdout.write('Getting playlist contents.')
        playlists = api.get_all_user_playlist_contents()
        for playlist in playlists:
            p = Playlist()
            p.pid = playlist['id']
            p.name = playlist['name']
            p.save()
            for entry in playlist['tracks']:
                try:
                    track = Track.objects.get(stream_id=entry['trackId'])
                    pc = PlaylistConnection()
                    pc.playlist = p
                    pc.track = track
                    pc.save()
                except Exception:
                    print "Not found."

        self.stdout.write('Library saved!')
#Get the file name
#path = tkFileDialog.askopenfilename()

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

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

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

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

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

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

	#Create arrays for the artists and tracks in the gmusic playlist
	gArtists = []
	gTracks = []
Example #53
0
class GMusic(object):
    def __init__(self):
        self.authenticated = False
        self.all_access = False
        self.library_loaded = False
        self.all_songs = []
        self.letters = {}
        self.artists = {}
        self.albums = {}
        self.genres = {}
        self.tracks_by_letter = {}
        self.tracks_by_artist = {}
        self.tracks_by_album = {}
        self.tracks_by_genre = {}
        self._device = None
        self._webclient = Webclient(debug_logging=False)
        self._mobileclient = Mobileclient(debug_logging=False)
        self._playlists = []
        self._playlist_contents = []
        self._stations = []

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

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

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

        else:
            return self.all_songs

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        self.library_loaded = True

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

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

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

        return self._playlists

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

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

        return tracks

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

        return playlist

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

        return self._stations

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

        return tracks

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

        return genres

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

        return station

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

        return results

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

        return results

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

        return results

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

        return track

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

        return tracks

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

        return stream_url
Example #54
0
class MusicLibrary(object):
    """This class reads information about your Google Play Music library"""

    def __init__(self, username=None, password=None,
                 true_file_size=False, scan=True, verbose=0):
        self.verbose = False
        if verbose > 1:
            self.verbose = True

        self.__login_and_setup(username, password)

        self.__artists = {} # 'artist name' -> {'album name' : Album(), ...}
        self.__albums = [] # [Album(), ...]
        self.__tracks = {}
        self.__playlists = {}
        if scan:
            self.rescan()
        self.true_file_size = true_file_size

    def rescan(self):
        """Scan the Google Play Music library"""
        self.__artists = {} # 'artist name' -> {'album name' : Album(), ...}
        self.__albums = [] # [Album(), ...]
        self.__tracks = {}
        self.__playlists = {}
        self.__aggregate_albums()

    def __login_and_setup(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)
            self.config = ConfigParser.ConfigParser()
            self.config.read(cred_path)
            username = self.config.get('credentials','username')
            password = self.config.get('credentials','password')
            global deviceId
            deviceId = self.config.get('credentials','deviceId')
            if not username or not password:
                raise NoCredentialException(
                    'No username/password could be read from config file'
                    ': %s' % cred_path)
            if not deviceId:
                raise NoCredentialException(
                    'No deviceId could be read from config file'
                    ': %s' % cred_path)
            if deviceId.startswith("0x"):
                deviceId = deviceId[2:]

        self.api = GoogleMusicAPI(debug_logging=self.verbose)
        log.info('Logging in...')
        self.api.login(username, password, deviceId)
        log.info('Login successful.')

    def __aggregate_albums(self):
        """Get all the tracks and playlists in the library, parse into relevant dicts"""
        log.info('Gathering track information...')
        tracks = self.api.get_all_songs()
        for track in tracks:
            log.debug('track = %s' % pp.pformat(track))

            # Prefer the album artist over the track artist if there is one
            artist_name = formatNames(track['albumArtist'])
            if artist_name.strip() == '':
                artist_name = formatNames(track['artist'])
            if artist_name.strip() == '':
                artist_name = 'Unknown'

            # Get the Artist object, or create one if it doesn't exist
            artist = self.__artists.get(artist_name.lower(), None)
            if not artist:
                artist = Artist(self, artist_name)
                self.__artists[artist_name.lower()] = artist

            # Get the Album object, or create one if it doesn't exist
            album = artist.get_album(formatNames(track['album']))
            if not album:
                album = Album(self, track['album'])
                self.__albums.append(album) # NOTE: Current no purpose other than to count
                artist.add_album(album)

            # Add track to album
            album.add_track(track)

            # Add track to list of all tracks, indexable by track ID
            if 'id' in track:
                self.__tracks[track['id']] = track

        log.debug('%d tracks loaded.' % len(tracks))
        log.debug('%d artists loaded.' % len(self.__artists))
        log.debug('%d albums loaded.' % len(self.__albums))

        # Add all playlists
        playlists = self.api.get_all_user_playlist_contents()
        for pldata in playlists:
            playlist = Playlist(self, pldata)
            self.__playlists[playlist.dirname.lower()] = playlist
        log.debug('%d playlists loaded.' % len(self.__playlists))

    def get_artists(self):
        """Return list of all artists in the library"""
        return self.__artists.values()

    def get_artist(self, name):
        """Return the artist from the library with the specified name"""
        return self.__artists.get(name.lower(), None)

    def get_playlists(self):
        """Return list of all playlists in the library"""
        return self.__playlists.values()

    def get_playlist(self, name):
        """Return the playlist from the library with the specified name"""
        return self.__playlists.get(name.lower(), None)

    def get_track(self, trackid):
        """Return the track from the library with the specified track ID"""
        return self.__tracks.get(trackid, None)

    def cleanup(self):
        pass
    else:
        f = open("email.txt", "w")
        email = raw_input("Email:    ")
        f.write(email)
    password = getpass.getpass("Password:   "******"name") for x in playlist_list])

playlists = mc.get_all_user_playlist_contents()
choice = raw_input("What playlist:   ")

for playlist in playlists:
    if str(playlist.get("name")) == choice:
        chosen = playlist

tracks = []

for x in chosen.get("tracks"):
    tracks.append(x)

if raw_input("Shuffle?\n").lower() == "yes":
    random.shuffle(tracks)

for song in tracks:
Example #56
0
class Session(object):
    def __init__(self):
        self.api = None
        self.user = None
        self.lib_albums = {}
        self.lib_artists = {}
        self.lib_tracks = {}
        self.lib_updatetime = 0
        self.sitdata = []
        self.sitbyid = {}
        self.sitdataupdtime = 0

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return ret

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

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

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

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

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

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

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

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

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

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

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

        tr = [_parse_track(i["track"]) for i in data["song_hits"]]
        print("track ok", file=sys.stderr)
        ar = [_parse_artist(i["artist"]) for i in data["artist_hits"]]
        print("artist ok", file=sys.stderr)
        al = [_parse_album(i["album"]) for i in data["album_hits"]]
        print("album ok", file=sys.stderr)
        # self.dmpdata("Search playlists", data['playlist_hits'])
        try:
            pl = [_parse_splaylist(i) for i in data["playlist_hits"]]
        except:
            pl = []
        print("playlist ok", file=sys.stderr)
        return SearchResult(artists=ar, albums=al, playlists=pl, tracks=tr)
Example #57
0
class Player(object):

    def __init__(self, email, password, device_id):
        self.api = Mobileclient()
        self.vlc = Instance()
        self.loaded_tracks = []
        self.playing = False
        self.thread_running = False
        
        self.api.login(email, password, device_id)
        if os.path.isfile("songs.json"):
            # Load from file
            print("Found songs data.")
            with open('songs.json') as input_file:
                self.song_library = json.load(input_file)
        else:
            self.song_library = self.api.get_all_songs()
            # Save to file
            with open('songs.json', 'w') as output_file:
                json.dump(self.song_library, output_file)    
        
    def load_playlist(self, name):
        name = name.strip().lower()
        print("Looking for...", name)
        if os.path.isfile("playlists.json"):
            # Load from file
            print("Found playlist data.")
            with open('playlists.json') as input_file:
                self.playlists = json.load(input_file)
        else:
            self.playlists = self.api.get_all_user_playlist_contents()
            # Save to file
            with open('playlists.json', 'w') as output_file:
                json.dump(self.playlists, output_file)
            
        self.loaded_tracks = []
        for playlist_dict in self.playlists:
            playlist_name = playlist_dict['name'].strip().lower()
            if (playlist_name == name) or (name in playlist_name):
                print("Found match...", playlist_dict['name'])
                for track_dict in playlist_dict['tracks']:
                    self.loaded_tracks.append(track_dict)
                return playlist_dict['name']
            else:
                print("Found...", playlist_dict['name'])
        return None
 
    def end_callback(self, event, track_index):
        if track_index < len(self.loaded_tracks):
            self.play_song(self.loaded_tracks[track_index])
            event_manager = self.player.event_manager()
            event_manager.event_attach(EventType.MediaPlayerEndReached, self.end_callback, track_index + 1)
            self.playing = True
        else:
            self.playing = False

    def start_playlist(self):
        if len(self.loaded_tracks) > 0:
            self.play_song(self.loaded_tracks[0])
        
            if len(self.loaded_tracks) > 1:
                event_manager = self.player.event_manager()
                event_manager.event_attach(EventType.MediaPlayerEndReached, self.end_callback, 1)
  
    def play_song(self, song_dict):
        stream_url = self.api.get_stream_url(song_dict['trackId'])
        self.player = self.vlc.media_player_new()
        media = self.vlc.media_new(stream_url)
        self.player.set_media(media)
        self.player.play()

        song_string = ""
        if (song_dict['source'] == '2'):
            song_string = self.get_song_details(song_dict)
        else:
            song_string = self.get_local_song_details(song_dict['trackId'])

        print("Playing...",song_string)
        
        if enable_display:
            scrollphat.clear()
            scrollphat.write_string(" "*5+song_string)

            if not self.thread_running:
                thread = Thread(target=self.scroll_string)
                thread.start()

        self.playing = True

    def scroll_string(self):
        self.thread_running = True
        while self.thread_running:
            scrollphat.scroll()
            time.sleep(0.1)

    def stop(self):
        if self.player != None:
            self.player.stop()
        if enable_display:
            scrollphat.clear()
        self.thread_running = False
        self.playing = False

    def get_local_song_details(self, track_id):
        for song_dict in self.song_library:
            if track_id == song_dict['id']:
                return song_dict['albumArtist']+" - "+song_dict['title']

    def get_song_details(self, song_dict):
        return song_dict['track']['albumArtist']+" - "+song_dict['track']['title']
Example #58
0
class MusicLibrary(object):
    'Read information about your Google Music library'

    def __init__(self, username=None, password=None,
                 true_file_size=False, scan=True, verbose=0):
        self.verbose = False
        if verbose > 1:
            self.verbose = True

        self.__login_and_setup(username, password)

        self.__artists = {} # 'artist name' -> {'album name' : Album(), ...}
        self.__albums = [] # [Album(), ...]
        self.__tracks = {}
        self.__playlists = {}
        if scan:
            self.rescan()
        self.true_file_size = true_file_size

    def rescan(self):
        self.__artists = {} # 'artist name' -> {'album name' : Album(), ...}
        self.__albums = [] # [Album(), ...]
        self.__tracks = {}
        self.__playlists = {}
        self.__aggregate_albums()

    def __login_and_setup(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)
            self.config = ConfigParser.ConfigParser()
            self.config.read(cred_path)
            username = self.config.get('credentials','username')
            password = self.config.get('credentials','password')
            global deviceId
            deviceId = self.config.get('credentials','deviceId')
            if not username or not password:
                raise NoCredentialException(
                    'No username/password could be read from config file'
                    ': %s' % cred_path)
            if not deviceId:
                raise NoCredentialException(
                    'No deviceId could be read from config file'
                    ': %s' % cred_path)

        self.api = GoogleMusicAPI(debug_logging=self.verbose)
        log.info('Logging in...')
        self.api.login(username, password)
        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:
            log.debug('track = %s' % pp.pformat(track))
            # Prefer the album artist over the track artist if there is one:
            artist = formatNames(track['albumArtist'].lower())
            if artist.strip() == '':
                artist = formatNames(track['artist'].lower())
            # Get the Album object if it already exists:
            key = '%s|||%s' % (formatNames(artist), formatNames(track['album'].lower()))
            album = all_artist_albums.get(key, None)
            if not album:
                # New Album
                if artist == '':
                    artist = 'unknown'
                album = all_artist_albums[key] = Album(
                    self, formatNames(track['album'].lower()))
                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)
            if 'id' in track:
                self.__tracks[track['id']] = track
        log.debug('%d tracks loaded.' % len(tracks))
        log.debug('%d artists loaded.' % len(self.__artists))
        log.debug('%d albums loaded.' % len(self.__albums))
        playlists = self.api.get_all_user_playlist_contents()
        for playlist in playlists:
            name = formatNames(playlist['name'].lower())
            log.debug('Playlist %s' % name)
            self.__playlists[name] = []
            entries = playlist['tracks']
            for entry in entries:
                log.debug('Playlist entry = %s' % pp.pformat(entry))
                if 'track' in entry:
                    track = entry['track']
                    track['id'] = entry['trackId']
                else:
                    track = self.__tracks[entry['trackId']]
                self.__playlists[name].append(track)
        log.debug('%d playlists loaded.' % len(self.__playlists))

    def get_artists(self):
        return self.__artists

    def get_playlists(self):
        return self.__playlists

    def get_albums(self):
        return self.__albums

    def get_artist_albums(self, artist):
        log.debug(artist)
        return self.__artists[artist]

    def cleanup(self):
        pass
Example #59
0
def get_google():
    db_path = 'ServerDatabase.db'
    # Columns in the table XFILES
    schema = ['Id', 'RootId', 'FileHandle', 'RevisionAdded',
              'RevisionDeleted', 'FieldUpdateRevision', 'DisplayName',
              'DisplayPath', 'IsFolder', 'Size', 'FileType', 'Usage',
              'DateCreated', 'DateModified', 'TranscodingType',
              'MusicChannels', 'MusicName', 'MusicAlbum', 'MusicArtist',
              'MusicAlbumArtist', 'MusicComposer', 'MusicComment',
              'MusicGenre', 'MusicYear', 'MusicDuration',
              'MusicTrackCount', 'MusicTrackNumber', 'MusicDiscCount',
              'MusicDiscNumber', 'MusicCompilation', 'MusicBitRate',
              'MusicSampleRate', 'MusicBpm', 'MusicAlbumArtStart',
              'MusicAlbumArtSize', 'MusicAlbumArtType', 'MusicAlbumArt',
              'MusicStart', 'MusicLength', 'MD5Hash', 'ServerId',
              'MusicRating', 'MusicPlayCount', 'MusicDateAdded',
              'MusicDatePlayed', 'MusicUploadStatus',
              'MusicUploadSelected', 'MusicLastScanned', 'MusicIsPodcast',
              'StoreType', 'MusicLabelCode', 'MusicUitsJson',
              'MusicContentRating', 'MusicAdditionalMetadataJson',
              'DeleteAfterUpload', 'RowLastModified',
              'MusicUploadStatusLastModified']
    
    # Establish a connection to a local Google Play MusicManager sqlite
    # database, with local file paths for any songs that I uploaded to Google Play
    conn = connect(db_path)
    c = conn.cursor()
    
    # The first line of secret_google.txt is a Google Music user email,
    # the second line the user's password
    with open('secret_google.txt') as f:
        r = [l.strip() for l in f.readlines()]
    
    api = Mobileclient()
    api.login(r[0], r[1], Mobileclient.FROM_MAC_ADDRESS)
    
    # All playlists to record start with a three digit integer
    format_check = compile('^[0-9]{3}(?::|$)')
    # Note the clash between the search string ':' and the syntax of
    # non-capturing groups in regexes (?:...)
    
    inplaylists = api.get_all_user_playlist_contents()
    playlists = [p for p in inplaylists if format_check.search(p['name']) is not None]
    playlists = sorted(playlists, key = lambda k: k['name'])
    playlists = playlists[1:] + playlists[:1]
    
    google = '2017-01--'

    keys = list()
    keys.append(google)
    
    out = '''
    
    
    <li><div id="{0}">
      <strong><a href="#" id="{0}-title">{1}</a></strong>
      <div id="{0}-body" style="display: none;">\n<ul class="space-before space-after">'''.format(google, google.replace('--', '&ndash;'))
    
    for playlist in playlists:
        pn = playlist['name']

        entries = list()
        for t in playlist['tracks']:
            if t['source'] == '1':
                ## 'trackId' corresponds to column 'ServerId' in table XFILES
                selector = "SELECT * FROM XFILES WHERE ServerId='{}'"
                c.execute(selector.format(t['trackId']))
                matches = c.fetchall()
                if len(matches) == 1:
                    m = matches[0]
                    lookup = dict(zip(schema, m))
                    s = title_by_artist.format(lookup['MusicName'], lookup['MusicArtist'])
                    entries.append('<li>{0}</li>'.format(s))
                else:
                    print('Weirdness in playlist {}. Track info:'.format(playlist['name']))
                    print(t)

            else:
                track = t['track']
                s = title_by_artist.format(track['title'], track['artist'])
                entries.append('<li>{0}</li>'.format(s))
        p_txt   = '\n'.join(entries)
        p_title = ''.join([c for c in pn.replace(' ', '-') if c in alphanum])
        keys.append(p_title)
        out = add_to_out(out, '''

    <li><div id="{1}">
      <a href="#" id="{1}-title">{0}</a>

      <div id="{1}-body" style="display: none;">
    <ol class="space-before space-after">
      {2}
    </ol>
  </div> <!-- #{1}-body -->
</div> <!-- #{1} -->
</li>'''.format(pn, p_title, p_txt))

    out = add_to_out(out, '''
  </div> <!-- #{0}-body -->
</div> <!-- #{0} -->
</li>'''.format(google))

    return([out, keys])