Example #1
0
def sr_geturl(songkey):
    try:
        stream_url = Mobileclient.get_stream_url(api, songkey)
        return(stream_url)
    except Exception as e:
        print(e)
        reconnect()
Example #2
0
class Music:
    def __init__(self):
        self.index = 0
        self.tracks = []
        self.api = Mobileclient()
        print("logged")
        self.api.login('*****@*****.**', 'romgroGMAIL95',
                       Mobileclient.FROM_MAC_ADDRESS)

    def load_random(self):
        stations = self.api.get_all_stations()
        station = random.choice(stations)
        self.tracks = self.api.get_station_tracks(station["id"])

    def play_random(self):
        url = self.api.get_stream_url(self.tracks[self.index]['nid'],
                                      device_id=None,
                                      quality=u'hi')
        self.player = vlc.MediaPlayer(url)
        self.player.play()

    def update(self):
        # todo update
        pass

    def next_song(self):
        self.index += 1
        self.play_random()
        pass
Example #3
0
class GoogleMusic(Source):
    def __init__(self, library, username, password):
        Source.__init__(self, library, SourceType.GOOGLE)
        self.GOOGLE_DEVICE_ID = None

        print username
        print password

        self.client = Mobileclient()
        logged_in = self.client.login(username, password, Mobileclient.FROM_MAC_ADDRESS)
        print "Google logged in:", logged_in

        DList = self.client.get_registered_devices()
        self.GOOGLE_DEVICE_ID = DList[0]["id"]
        if self.GOOGLE_DEVICE_ID[:2] == '0x':
            self.GOOGLE_DEVICE_ID = self.GOOGLE_DEVICE_ID[2:]

        print self.GOOGLE_DEVICE_ID

        #>testing
        # self.get_stream_URL("47b9d52c-9d66-3ff2-94d4-3ae55c0d2acc")

    def get_stream_URL(self, song_id):
        return self.client.get_stream_url(song_id, self.GOOGLE_DEVICE_ID)

    def sync(self):
        gmusic_tracks = self.client.get_all_songs()
        for track in gmusic_tracks:
            art = ''
            try:
                art = track['albumArtRef'][0]['url']
            except KeyError:
                art = ''
            self.library.insert_track(track['title'], track['album'], track['artist'], self._source, str(track['id']), track['trackNumber'], art)
Example #4
0
class Client(object):
    def __init__(self):
        self.client = Mobileclient()
        self.client.login(config.gmusic['email'], config.gmusic['password'],
                          Mobileclient.FROM_MAC_ADDRESS)

    def search_songs(self, query_str):
        song_hits = self.client.search(unicode(query_str), 8)['song_hits']
        songs = []
        for song_hit in song_hits:
            songs.append({
                'title': song_hit['track']['title'],
                'artist': song_hit['track']['artist'],
                'album': song_hit['track']['album'],
                'nid': song_hit['track']['nid']
            })

        return songs

    def get_song_url(self, song_nid):
        song_id = self.__prepare_song_id(song_nid)
        return self.client.get_stream_url(song_id)

    def get_song_info(self, song_nid):
        song_id = self.__prepare_song_id(song_nid)
        return self.client.get_track_info(song_id)

    def __prepare_song_id(self, song_nid):
        return 'T{0}'.format(song_nid)
def download_album(username, password, artist_name, album_name):
    api = Mobileclient()
    api.login(username, password, Mobileclient.FROM_MAC_ADDRESS)

    library = api.get_all_songs()
    songs = [s for s in library if s['albumArtist'] == artist_name and s['album'] == album_name]

    if len(songs) == 0:
        print('Error: Album not found', file=sys.stderr)
        return

    device_id = api.get_registered_devices()[0]['id'].replace('0x', '')
    dname = slugify(unicode(album_name))
    os.mkdir(dname)
    
    # download songs
    for song in tqdm(songs, desc='Downloading'):
        fname = slugify(song['title'])
        mpg_name = os.path.join(dname, fname + '.mpg')
        mp3_name = os.path.join(dname, fname + '.mp3')

        url = api.get_stream_url(song['id'], device_id=device_id)
        response = requests.get(url)

        # first save as MPEG video
        with open(mpg_name, 'wb') as fout:
            for chunk in response.iter_content(chunk_size=128):
                fout.write(chunk)

        # call FFMPEG to convert to MP3
        os.system(' '.join([FFMPEG_CMD] + FFMPEG_ARGS).format(
            input=mpg_name,
            output=mp3_name,
            title=song['title'],
            artist=song['albumArtist'],
            album=song['album'],
            track=song['trackNumber']))

        os.remove(mpg_name)

    # download album art
    art_name = os.path.join(dname, dname + '.png')
    album_info = api.get_album_info(songs[0]['albumId'], include_tracks=False)
    response = requests.get(album_info['albumArtRef'])
    t = magic.from_buffer(response.content, mime=True)

    if t == 'image/jpeg':
        ext = '.jpg'
    elif t == 'image/png':
        ext = '.png'
    else:
        print('Unknown MIME type: {}'.format(t), file=sys.stderr)
        ext = '.wat'

    with open(os.path.join(dname, dname + ext), 'wb') as fout:
        fout.write(response.content)
Example #6
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 #7
0
class Jukebox:
    def __init__(self, credentials_file):
        with open(credentials_file) as f:
            credentials = yaml.load(f)
        email = credentials['email']
        password = credentials['password']
        android_id = credentials['android_id']
        self.api = Mobileclient()
        self.__authenticated = self.api.login(email, password, android_id)
        self.player = vlc.MediaPlayer()

    def search(self, query):
        self.__is_authenticated()
        return self.api.search(query)

    def get_track_url(self, track_id):
        self.__is_authenticated()
        return self.api.get_stream_url(track_id)

    def set_track(self, filepath):
        self.player.set_media(
            vlc.get_default_instance().media_new('songs/' + filepath + '.mp3'))

    def play(self):
        self.__is_authenticated()
        self.player.play()

    def pause(self):
        self.__is_authenticated()
        self.player.pause()

    def stop(self):
        self.__is_authenticated()
        self.player.stop()

    def get_length(self):
        self.__is_authenticated()
        return self.player.get_length()

    def get_current_time(self):
        self.__is_authenticated()
        return self.player.get_time()

    def is_playing(self):
        self.__is_authenticated()
        return self.player.is_playing()

    def __is_authenticated(self):
        if not self.__authenticated:
            raise NotAuthenticatedError

    def create_station(self, store_id):
        self.__is_authenticated()
        station_id = self.api.create_station('station', track_id=store_id)
        station_tracks = self.api.get_station_tracks(station_id)
        return station_tracks
Example #8
0
def download_store_track(api: gmusicapi.Mobileclient, full_path, track, device_id):
    dirname = os.path.dirname(full_path)
    fp, temp_path = tempfile.mkstemp(dir=dirname)
    try:
        stream_url = api.get_stream_url(track['nid'], device_id=device_id)
        urllib.request.urlretrieve(stream_url, temp_path)
        os.rename(temp_path, full_path)
    finally:
        if os.path.exists(temp_path):
            os.unlink(temp_path)
Example #9
0
class GoogleMusic(object):
    def __init__(self):
        self.webclient = Webclient()
        self.mobileclient = Mobileclient()

    def is_authenticated(self):
        if not self.webclient.is_authenticated():
            if self.mobileclient.is_authenticated():
                return True

        return False

    def login(self, username, password):
        if not self.is_authenticated():
            try:
                self.mobileclient.login(username, password, Mobileclient.FROM_MAC_ADDRESS)
                self.webclient.login(username, password)
            except Exception as e:
                raise Exception('Couldn\'t log into Google Music: ' + e.message)

    def search(self, query, kind):
        if self.is_authenticated():
            results = self.mobileclient.search(query)[kind + '_hits']

            return results

    def get_track(self, store_id):
        return self.mobileclient.get_track_info(store_id)

    def save_stream(self, track, destination):
        if self.is_authenticated():
            with open(destination, 'w+b') as stream_file:
                url = self.mobileclient.get_stream_url(track.get('storeId'))

                stream_file.truncate(0)
                stream_file.seek(0, 2)
                audio = self.webclient.session._rsession.get(url).content
                stream_file.write(audio)

            tag = easyid3.EasyID3()
            tag['title'] = track.get('title').__str__()
            tag['artist'] = track.get('artist').__str__()
            tag['album'] = track.get('album').__str__()
            tag['date'] = track.get('year').__str__()
            tag['discnumber'] = track.get('discNumber').__str__()
            tag['tracknumber'] = track.get('trackNumber').__str__()
            tag['performer'] = track.get('albumArtist').__str__()
            tag.save(destination)

            tag = mp3.MP3(destination)
            tag.tags.add(
                id3.APIC(3, 'image/jpeg', 3, 'Front cover', urllib.urlopen(track.get('albumArtRef')[0].get('url')).read())
            )
            tag.save()
Example #10
0
class AudioStream:
    __username = configuration.get('google_username')
    __password = configuration.get('google_password')
    __track_prefetch = 15
    __client = None
    __playlist = []

    def __init__(self, station_id = 'IFL'):
        self.__client = Mobileclient()
        self.__client.login(self.__username, self.__password, Mobileclient.FROM_MAC_ADDRESS)
        self.__playlist = self.__fetchTrackIDs(station_id)

    def __del__(self):
        if self.__client:
            self.__client.logout()

    def __fetchTrackIDs(self, station_id):
        if not self.__client or not self.__client.is_authenticated():
            logger.error("Client is not authenticated!")
            return []

        tracklist = self.__client.get_station_tracks(station_id, num_tracks=self.__track_prefetch)
        logger.info("Received tracks: %r" % json.dumps(tracklist))

        # Filter out explicit tracks, where non-explicit is explicitType=2
        tracklist = [track for track in tracklist if not 'explicitType' in track or track['explicitType'] == "2"]
        logger.info("Non-explicit tracks: %r" % json.dumps(tracklist))

        # Fetch both song IDs and Nautilus (old) IDs
        songids = [track['id'] for track in tracklist if 'id' in track]
        nautids = [track['nid'] for track in tracklist if 'nid' in track]

        return songids + nautids

    def pop(self):
        while self.__playlist:
            track_id = self.__playlist.pop()

            try:
                stream_url = self.__client.get_stream_url(track_id, quality='low')
                return stream_url
            except(exceptions.CallFailure):
                logger.warning("Failed to fetch Stream URL for ID %s" % track_id)

            raise IndexError("pop from empty list")

    def reverse(self):
        # Reverse just returns itself, since the playlist is already chaos
        return self

    def __len__(self):
        return len(self.__playlist)
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', [])}
Example #12
0
def play_song():
    """
    Need OAuth creds first:
    from gmusicapi import Mobileclient
    api = Mobileclient()
    api.perform_oauth()
    # do instructions, it will save creds to storage.
    """
    api = Mobileclient()
    api.oauth_login(Mobileclient.FROM_MAC_ADDRESS)
    library = api.get_all_songs()
    track_id = library[0]['id']
    url = api.get_stream_url(track_id)
    p = vlc.MediaPlayer(url)
    p.play()
Example #13
0
def main():
    api = Mobileclient()
    print('login as {} to Google Music...'.format(CONFIG['login']))
    if api.login(CONFIG['login'], CONFIG['password'], CONFIG['device_id']):
        try:
            if not api.is_subscribed:
                raise AssertionError(
                    'You has not Google Music subscription :(')

            print('Reading all songs...')
            lib = api.get_all_songs()
            albums = CONFIG['albums']
            for artist in albums:
                print('Artist: {}'.format(artist))
                for album in albums[artist]:
                    print('Album: {}'.format(album))
                    tracks = sorted([
                        t for t in lib
                        if t['album'] == album and t['artist'] == artist
                    ],
                                    key=lambda t: t['trackNumber'])
                    if tracks:
                        dir_name = os.path.join(
                            CONFIG['root_dir'], fix_file_name(artist),
                            "{} {}".format(tracks[0]['year'],
                                           fix_file_name(album)))
                        if not os.path.exists(dir_name):
                            os.makedirs(dir_name)
                        for tr in tracks:
                            if 'albumArtRef' in tr:
                                i = 0
                                for artRef in tr['albumArtRef']:
                                    img_file_name = 'cover.{}.jpg' if i else 'cover.jpg'
                                    full_path = os.path.join(
                                        dir_name, img_file_name)
                                    full_path = fix_path_name(full_path)
                                    if not os.path.exists(full_path):
                                        wget.download(artRef['url'], full_path)
                            file_name = '{:02d} {}.mp3'.format(
                                tr['trackNumber'], tr['title'])
                            file_name = fix_file_name(file_name)
                            full_path = os.path.join(dir_name, file_name)
                            if not os.path.exists(full_path):
                                url = api.get_stream_url(tr['id'])
                                wget.download(url, full_path)
                                update_tags(tr, full_path)
        finally:
            api.logout()
Example #14
0
def ask_for_song(song):
    api = Mobileclient()
    # mm.perform_oauth('C:\\Users\\mitsu\\Documents\\Python\\projects\\gmusicapi-develop\\oauth.cred')
    api.oauth_login(Mobileclient.FROM_MAC_ADDRESS, 'oauth.cred')
    api.login

    songs = api.search(song, 2)

    # print(json.dumps((library), indent=4))

    title = songs['song_hits'][0]['track']['title']
    artist = songs['song_hits'][0]['track']['artist']
    song_id = songs['song_hits'][0]['track']['storeId']
    stream_url = api.get_stream_url(song_id)

    track = [title, artist, stream_url]

    api.logout()

    return track
Example #15
0
class GClient:
    def __init__(self):
        self._gclient = Mobileclient()
        self._credentials = self._get_credentials()

    def _get_credentials(self):
        config = configparser.ConfigParser()
        config.read(os.path.expanduser(CREDENTIALS_FILE))
        credentials = {}
        credentials['username'] = config.get('credentials', 'username')
        credentials['password'] = config.get('credentials', 'password')
        return credentials

    def login(self):
        logged_in = self._gclient.login(self._credentials['username'],
                                        self._credentials['password'],
                                        Mobileclient.FROM_MAC_ADDRESS)
        if not logged_in:
            print("Failed to log in.")
            exit(1)

    def get_device_id(self):
        # Get all devices, pick the first, get its ID, strip the leading '0x'
        self.device_id = self._gclient.get_registered_devices()[0]['id'][2:]
        return self.device_id

    def get_station(self):
        stations = [
            station for station in self._gclient.get_all_stations()
            if station['inLibrary']
        ]
        return stations[0]

    def get_tracks(self):
        station = self.get_station()
        tracks = self._gclient.get_station_tracks(station['id'])
        return tracks

    def get_stream_url(self, track_id):
        device_id = self.get_device_id()
        return self._gclient.get_stream_url(track_id, device_id)
class ClientHandler:

    def __init__(self, user):
        self.GOOGLE_DEVICE_ID = user.GOOGLE_DEVICE_ID
        self.SOUNDCLOUD_CLIENT_ID = user.SOUNDCLOUD_CLIENT_ID

        self.G_client = Mobileclient()

        logged_in = self.G_client.login(user.G_username,user.G_password)

        self.S_client = soundcloud.Client(client_id=user.SOUNDCLOUD_CLIENT_ID, client_secret=user.SOUNDCLOUD_CLIENT_SECRET_ID, username=user.S_username, password=user.S_password)

    def get_stream_URL(self, sid, location):
        if location == 'G':
            return self.G_client.get_stream_url(sid[2:],self.GOOGLE_DEVICE_ID)
        elif location.upper() == 'S':
            return self.S_client.get('/tracks/' + str(sid[2:])).stream_url + "?client_id=" + self.SOUNDCLOUD_CLIENT_ID
        elif location == 'L':
            return sid[2:]
        else:
            print "Error: Track not found"
Example #17
0
class Kotone:

    def __init__(self, device_id: str, cred_mc: oauth2client.client.OAuth2Credentials, cred_mm: oauth2client.client.OAuth2Credentials):
        self._mc = Mobileclient()
        if not self._mc.oauth_login(device_id, cred_mc):
            raise RuntimeError('Mobileclient login failed')
        self._mm = Musicmanager()
        if not self._mm.login(cred_mm, _UPLOADER_ID, _UPLOADER_NAME):
            raise RuntimeError('Musicmanager login failed')

    def get_songs(self):
        return self._mc.get_all_songs()

    def download_song(self, song_id: str) -> Tuple[str, bytes]:
        return self._mm.download_song(song_id)

    def stream_url(self, song_id: str) -> str:
        return self._mc.get_stream_url(song_id)

    def upload(self, paths):
        return self._mm.upload(paths)
Example #18
0
class GoogleMusicProvider:
    api = None

    def __init__(self, login, password, android_id, *args):
        self.api = Mobileclient()
        auth = self.api.login(login, password, android_id)
        if not auth:
            self.api.get
        print('GPM login: {0}'.format(auth))

    def search(self, query):
        return self.api.search(query, max_results=1)['song_hits'][0]['track']

    def getUrl(self, id):
        return self.api.get_stream_url(id)

    def add_playlist(self, name):
        return self.api.create_playlist(name)

    def add_to_playlist(self, trackId, playlistId):
        self.api.add_songs_to_playlist(playlist_id=playlistId,
                                       song_ids=trackId)
Example #19
0
def home(request):

    sync = request.params.get('sync', None)
    api = Mobileclient()

    api.login(GOOGLE_EMAIL, GOOGLE_MUSIC_PASS, Mobileclient.FROM_MAC_ADDRESS)

    if os.path.isfile('songs.json') and not sync:
        f = open('songs.json', 'r')
        songs = f.read()
        songs = json.loads(songs)
    else:
        songs = api.get_all_songs()
        songs = list(reversed(resort_by_added(songs)))

        f = open('songs.json', 'w')
        f.write(json.dumps(songs, indent=4, separators=(',', ': ')))
        f.close()

    result = []
    back = timedelta(days=10)
    now = datetime.now()

    for track in songs:
        d = get_datetime(track['creationTimestamp'])
        if (now - d) < back:
            result.append({
                'name':
                track['title'],
                'url':
                api.get_stream_url(track['id'], GOOGLE_DEVICE_ID),
                'date':
                d.strftime("%a %d-%m-%Y %H:%M")
            })

    master = get_renderer('templates/master.pt').implementation()

    return {'song_list': result, 'title': "Song list", 'main': master}
Example #20
0
class GoogleMusic(Source):
    def __init__(self, library, username, password):
        Source.__init__(self, library, SourceType.GOOGLE)
        self.GOOGLE_DEVICE_ID = None

        print username
        print password

        self.client = Mobileclient()
        logged_in = self.client.login(username, password,
                                      Mobileclient.FROM_MAC_ADDRESS)
        print "Google logged in:", logged_in

        DList = self.client.get_registered_devices()
        self.GOOGLE_DEVICE_ID = DList[0]["id"]
        if self.GOOGLE_DEVICE_ID[:2] == '0x':
            self.GOOGLE_DEVICE_ID = self.GOOGLE_DEVICE_ID[2:]

        print self.GOOGLE_DEVICE_ID

        #>testing
        # self.get_stream_URL("47b9d52c-9d66-3ff2-94d4-3ae55c0d2acc")

    def get_stream_URL(self, song_id):
        return self.client.get_stream_url(song_id, self.GOOGLE_DEVICE_ID)

    def sync(self):
        gmusic_tracks = self.client.get_all_songs()
        for track in gmusic_tracks:
            art = ''
            try:
                art = track['albumArtRef'][0]['url']
            except KeyError:
                art = ''
            self.library.insert_track(track['title'], track['album'],
                                      track['artist'], self._source,
                                      str(track['id']), track['trackNumber'],
                                      art)
Example #21
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
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 GoogleMusicLogin():
    def __init__(self):
        import requests
        from requests.packages.urllib3.exceptions import InsecureRequestWarning
        from requests.packages.urllib3.exceptions import InsecurePlatformWarning
        from requests.packages.urllib3.exceptions import SNIMissingWarning
        requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
        requests.packages.urllib3.disable_warnings(InsecurePlatformWarning)
        requests.packages.urllib3.disable_warnings(SNIMissingWarning)
        self.gmusicapi = Mobileclient(debug_logging=False, validate=False, verify_ssl=False)
        #self.gmusicapi._session_class.lang = xbmc.getLanguage(xbmc.ISO_639_1, True)
        #utils.log(repr(xbmc.getLanguage(xbmc.ISO_639_1, True)))

    def checkCookie(self):
        # Remove cookie data if it is older then 7 days
        if utils.addon.getSetting('cookie-time'):
            import time
            if time.time() - float(utils.addon.getSetting('cookie-time')) > 3600*24*7:
                self.clearCookie()

    def checkCredentials(self):
        if not utils.addon.getSetting('username'):
            utils.addon.openSettings()
        if utils.addon.getSetting('password') and utils.addon.getSetting('password') != '**encoded**':
            import base64
            utils.addon.setSetting('encpassword',base64.b64encode(utils.addon.getSetting('password')))
            utils.addon.setSetting('password','**encoded**')

    def getApi(self):
        return self.gmusicapi

    def getStreamUrl(self, song_id, session_token, wentry_id):
        # retrieve registered device
        device_id = self.getDevice()
        # retrieve stream quality from settings
        quality = { '0':'hi','1':'med','2':'low' } [utils.addon.getSetting('quality')]
        utils.log("getStreamUrl songid: %s device: %s quality: %s"%(song_id, device_id, quality))

        return self.gmusicapi.get_stream_url(song_id, device_id, quality, session_token, wentry_id)

    def getDevice(self):
        return utils.addon.getSetting('device_id')

    def initDevice(self):
        device_id = self.getDevice()

        if not device_id:
            utils.log('Trying to fetch the device_id')
            self.login()
            try:
                devices = self.gmusicapi.get_registered_devices()
                if len(devices) == 10:
                    utils.log("WARNING: 10 devices already registered!")
                utils.log('Devices: '+repr(devices))
                for device in devices:
                    if device["type"] in ("ANDROID","PHONE","IOS"):
                        device_id = str(device["id"])
                        break
            except Exception as e:
                utils.log("ERROR: "+repr(e))

            if device_id:
                if device_id.lower().startswith('0x'): device_id = device_id[2:]
                utils.addon.setSetting('device_id', device_id)
                utils.log('Found device_id: '+device_id)
            else:
                utils.log('No Android device found in account')
                #self.language = utils.addon.getLocalizedString
                #dialog = xbmcgui.Dialog()
                #dialog.ok("", self.language(30111))

    def clearCookie(self):
        utils.addon.setSetting('logged_in-mobile', "")
        utils.addon.setSetting('authtoken-mobile', "")
        utils.addon.setSetting('device_id', "")
        utils.addon.setSetting('subscriber', "0")

    def logout(self):
        self.gmusicapi.logout()

    def login(self, nocache=False):
        if not utils.addon.getSetting('logged_in-mobile') or nocache:
            import base64

            utils.log('Logging in')
            self.checkCredentials()
            username = utils.addon.getSetting('username')
            password = base64.b64decode(utils.addon.getSetting('encpassword'))

            try:
                self.gmusicapi.login(username, password, self.getDevice() )
            except: pass
            if not self.gmusicapi.is_authenticated():
                try:
                    utils.log("Login in with device_id failed, trying with MAC")
                    self.gmusicapi.login(username, password, Mobileclient.FROM_MAC_ADDRESS)
                except Exception as e:
                    utils.log("ERROR: "+repr(e))

            if not self.gmusicapi.is_authenticated():
                utils.log("Login failed")
                utils.addon.setSetting('logged_in-mobile', "")
                self.language = utils.addon.getLocalizedString
                dialog = xbmcgui.Dialog()
                dialog.ok(self.language(30101), self.language(30102))
                raise
            else:
                utils.log("Login succeeded. Device id: "+self.gmusicapi.android_id)
                utils.addon.setSetting('device_id', self.gmusicapi.android_id)
                utils.addon.setSetting('logged_in-mobile', "1")
                utils.addon.setSetting('authtoken-mobile', self.gmusicapi.session._authtoken)
                import time
                utils.addon.setSetting('cookie-time', str(time.time()))
                utils.addon.setSetting('subscriber','1' if self.gmusicapi.is_subscribed else '0')

        else:

            utils.log("Loading auth from cache")
            self.gmusicapi.session._authtoken = utils.addon.getSetting('authtoken-mobile')
            self.gmusicapi.session.is_authenticated = True
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 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 #26
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 #27
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)
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 #29
0
    else:
        print("Acquiring song db from Google.")
        try:
            songs = api.get_all_songs()
            output = open('mysongfile.pkl', 'wb')
            pickle.dump(songs, output)
            output.close()
            return songs
        except gmusicapi.exceptions.NotLoggedIn:
            print("Whoops, not logged in.\n")
            self.musiclogin()
            getsongs(api)

musiclogin(api)
songs = getsongs(api)
while True:
    try:
        song = random.choice(songs)
        if song['genre'] in ["Audio Book", "Classical", "Doom Metal", "Opera", "Podcast", "Educational"]:
            print("You probably don't want to hear {s}. Picking another one.".format(s=song['title']))
            continue
        songurl = 'http' + api.get_stream_url(song['id']).lstrip('https')
        print("Playing " + song['title'] + " by " + song['artist'] + " from album " + song['album'] +".\n")
        player = subprocess.Popen(['mpg321', songurl])
        while player.poll() is None:
            time.sleep(1)
            continue
    except KeyboardInterrupt:
        input("Press Enter to play next song, ^C to exit.")
        continue
Example #30
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 #31
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
Example #32
0
class GMusic(object):
    def __init__(self):
        self.webclient = Webclient(debug_logging=False)
        self.mobileclient = Mobileclient(debug_logging=False)
        self.email = None
        self.password = None
        self.mc_authenticated = False
        self.wc_authenticated = False
        self.authenticated = False
        self.device = None
        self.all_songs = list()
        self.playlists = list()

    ###########################################################################
    # Name: authenticate()                                                    #
    # Description: Attempts to authenticate class Mobileclient and Webclient  #
    #              instances.                                                 #
    # Inputs: login credentials: email, password                              #
    # Outputs: returns True if both Mobileclient and Webclient auth is True   #
    ###########################################################################
    def authenticate(self, email=None, password=None):
        if email:
            self.email = email
        if password:
            self.password = password

        try:
            Log("Authenticating mobileclient...")
            self.mc_authenticated = self.mobileclient.login(self.email, self.password)
        except AlreadyLoggedIn:
            self.mc_authenticated = True

        try:
            Log("Authenticating webclient...")
            self.wc_authenticated = self.webclient.login(self.email, self.password)
        except AlreadyLoggedIn:
            self.wc_authenticated = True

        self.authenticated = self.mc_authenticated and self.wc_authenticated

        return self.authenticated

    ###########################################################################
    # Name: get_all_songs()                                                   #
    # Description: Returns a list of all songs                                #
    # Inputs: None                                                            #
    # Outputs: A list of all the songs a user owns.                           #
    ###########################################################################
    def get_all_songs(self):
        try:
            self.all_songs = self.mobileclient.get_all_songs()
        except NotLoggedIn:
            if self.authenticate():
                self.all_songs = self.mobileclient.get_all_songs()
            else:
                Log("LOGIN FAILURE")
                return

        return self.all_songs

    def get_all_playlists(self):
        try:
            self.playlists = self.mobileclient.get_all_playlist_ids()
        except NotLoggedIn:
            if self.authenticate():
                self.playlists = self.mobileclient.get_all_playlist_ids()
            else:
                Log("LOGIN FAILURE")
                return

        return self.playlists

    def get_stream_url(self, song_id):
        try:
            stream_url = self.mobileclient.get_stream_url(song_id)
        except NotLoggedIn:
            if self.authenticate():
                stream_url = self.mobileclient.get_stream_url(song_id)
            else:
                Log("LOGIN FAILURE")
                return

        return stream_url
Example #33
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)
Example #34
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 #35
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
        credentials = json.load(open('data/unlocked/credentials.json', 'r'))
        self.client.login(credentials['username'], credentials['password'], mac)

    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 #36
0
class MusicManager(object):
    def __init__(self):

        self.api = Mobileclient(validate=False, debug_logging=False)
        if config.GOOGLE_STREAMKEY is not None:
            self.api.login(config.GOOGLE_USERNAME, config.GOOGLE_PASSWORD, config.GOOGLE_STREAMKEY)
        else:
            self.api.login(config.GOOGLE_USERNAME, config.GOOGLE_PASSWORD, Mobileclient.FROM_MAC_ADDRESS)

        self.queue = []
        self.current_index = len(self.queue) - 1

        self.vlc = VlcManager()

        self.state_thread = Thread(target=self.check_state)
        self.state_thread.daemon = True
        self.state_thread.start()

    def play_song(self, id):
        song = self.queue_song(id)
        self.current_index = len(self.queue) - 1
        self.load_song()
        return song

    def queue_song(self, id):
        self.queue.append(self.getSongInfo(id))

    def play_radio_station(self, id):
        results = self.api.get_station_tracks(id, num_tracks=40)

        for song in results:
            song['albumArtRef'] = song['albumArtRef'][0]['url']
            if 'artistId' in song:
                song['artistId'] = song['artistId'][0]

        self.current_index = len(self.queue) - 1
        self.queue.append(results)
        self.load_song()

    def play_album(self, args):
        album = self.get_album_details(args)
        songs = []
        for index in range(len(album['tracks'])):
            song = album['tracks'][index]
            if index == 0:
                songs.append(self.play_song(song['nid']))
            else:
                songs.append(self.queue_song(song['nid']))
        return songs

    def queue_album(self, args):
        album = self.get_album_details(args)
        songs = []
        for song in album['tracks']:
            songs.append(self.queue_song(song['nid']))
        return songs

    def next(self):
        self.current_index += 1
        self.load_song()

    def prev(self):
        self.current_index -= 1
        self.load_song()

    def pause(self):
        self.vlc.vlc_pause()

    def resume(self):
        self.vlc.vlc_resume()

    def volume(self, val):
        self.vlc.vlc_volume(val)

    def delete(self, id):
        if id > self.current_index:
            del self.queue[id]
        elif id < self.current_index:
            del self.queue[id]
            self.current_index -= 1
        else:
            del self.queue[id]
            self.load_song()

    def go_to(self, id):
        self.current_index = id
        self.load_song()

    def load_song(self):
        if self.current_index < len(self.queue):
            song = self.queue[self.current_index]
            url = self.api.get_stream_url(song['nid'], config.GOOGLE_STREAMKEY)
            self.vlc.vlc_play(url)

    def check_state(self):
        while True:
            status = self.vlc.player.get_state()
            if status == vlc.State.Ended:
                if self.current_index != len(self.queue) - 1:
                    self.next()

            time.sleep(1)

    def get_status(self):

        status = self.vlc.vlc_status()

        # status['queue'] = self.queue[:]
        # for i in range(len(status['queue'])):
        #     status['queue'][i]['vlcid'] = i
        #     if i == self.current_index:
        #         status['queue'][i]['current'] = True
        #         status['current'] = status['queue'][i]
        if len(self.queue) > 0:
            status['current'] = self.queue[self.current_index]
        return status

    def get_queue(self):
        queue = self.queue[:]
        for i in range(len(queue)):
            queue[i]['vlcid'] = i

        return queue


    def search(self, query):
        results = self.api.search_all_access(query, max_results=50)

        results['artist_hits'] = [artist['artist'] for artist in results['artist_hits']]

        results['album_hits'] = [album['album'] for album in results['album_hits']]
        for album in results['album_hits']:
            album['artistId'] = album['artistId'][0]

        results['song_hits'] = [song['track'] for song in results['song_hits']]
        for song in results['song_hits']:
            song['albumArtRef'] = song['albumArtRef'][0]['url']
            if 'artistId' in song:
                song['artistId'] = song['artistId'][0]
        return results

    def get_album_details(self, id):
        results = self.api.get_album_info(album_id=id, include_tracks=True)
        results['artistId'] = results['artistId'][0]
        for song in results['tracks']:
            song['albumArtRef'] = song['albumArtRef'][0]['url']
            if 'artistId' in song:
                song['artistId'] = song['artistId'][0]
        return results

    def get_artist_details(self, id):
        results = self.api.get_artist_info(artist_id=id)
        for album in results['albums']:
            album['artistId'] = album['artistId'][0]

        for song in results['topTracks']:
            song['albumArtRef'] = song['albumArtRef'][0]['url']
            if 'artistId' in song:
                song['artistId'] = song['artistId'][0]
        return results


    def create_radio_station(self, name, id):
        if id[0] == 'A':
            station_id = self.api.create_station(name, artist_id=id)

        elif id[0] == 'B':
            station_id = self.api.create_station(name, album_id=id)

        else:
            station_id = self.api.create_station(name, track_id=id)

        return station_id

    def get_radio_stations(self):
        return self.api.get_all_stations()

    def flush(self):
        self.vlc.vlc_stop()
        self.queue = []

    def getSongInfo(self, id):
        song = self.api.get_track_info(id)
        song['albumArtRef'] = song['albumArtRef'][0]['url']
        if 'artistId' in song:
            song['artistId'] = song['artistId'][0]
        return song
Example #37
0
songs = api.get_all_songs()
for song in songs:
    dirName = normalizePath("%s - %s" % (song["artist"], song["album"]))
    dirPath = targetDir + "/" + dirName
    if not os.path.exists(dirPath):
        print("downloading to directory: " + dirPath)
        os.makedirs(dirPath)
    fileName = normalizePath(
        "%s. %s - %s.mp3" %
        (song["trackNumber"], song["artist"], song["title"]))
    filePath = dirPath + "/" + fileName
    if os.path.exists(filePath):
        print(fileName + " already exists, skipping")
        continue
    url = api.get_stream_url(song_id=song["id"], quality="hi")
    print("downloading: " + fileName)
    urlretrieve(url, filePath)

    audio = eyed3.load(filePath)
    if audio.tag is None:
        audio.tag = eyed3.id3.Tag()
        audio.tag.file_info = eyed3.id3.FileInfo(filePath)
    audio.tag.artist = song["artist"]
    audio.tag.album = song["album"]
    audio.tag.album_artist = song["artist"]
    audio.tag.title = song["title"]
    audio.tag.track_num = song["trackNumber"]
    audio.tag.save()

print("done.")
Example #38
0

# 3.) Store Results in workingLibraryOBJ
#--------------------------------------#
workingPlaylistOBJ = lManager.analyzePlaylist(workingPlaylist)


# 4.) Check Library for local copy , download if necessary / start playing 1st song
# -----------------------------------------------------------------------------------#
A2 = 0
while A2 < len(workingPlaylistOBJ['songIDS']):

	if workingPlaylistOBJ['songIDS'][A2] in localLibrary['EDM']:
		print( workingPlaylistOBJ['trackName'][A2] + " already exists in library" )
	else:
		wURL = api.get_stream_url( workingPlaylistOBJ['songIDS'][A2] , api.android_id , 'hi' )
		lManager.download( libDIR + "/EDM" , workingPlaylistOBJ['songIDS'][A2] + ".mp3" , wURL )
		localLibrary['EDM'][workingPlaylistOBJ['songIDS'][A2]] = workingPlaylistOBJ
		print( "loaded " + workingPlaylistOBJ['songIDS'][A2] + " into localLibrary" )

	#start playing 1st song
	if nowPlaying == False:
		fName = os.path.join( libDIR , "EDM" , workingPlaylistOBJ['songIDS'][A2] + ".mp3" )
		p1 = threading.Thread( target=lManager.playLocalSong , args=(fName,) , kwargs=None )
		p1.start()
		print( "\n[now playing - \"" + workingPlaylistOBJ['songIDS'][A2] +".mp3\"]" +" = " + workingPlaylistOBJ['trackName'][A2] + " - " + workingPlaylistOBJ['artistName'][A2] + "\n" )
		nowPlaying = True

	A2 = A2 + 1

p1.join() # wait for 1st song to finish
Example #39
0
 path = os.path.join(args.dir, artist, album)
 filename = os.path.join(path, filename)
 if os.path.isfile(filename):
     print('Track already downloaded: %s - %s.' % (artist, title))
     continue
 if not os.path.isdir(path):
     logging.debug('Creating directory for downloaded track: %s', path)
     os.makedirs(path)
 if args.wait:
     print('Waiting %.2f seconds between tracks.' % args.wait)
     sleep(args.wait)
 print(u'[%d/%d] Downloading %s - %s to %s.' % (i + 1, len(results), artist, title, filename))
 start = time()
 logging.debug('Started download at %s.', ctime(start))
 try:
     url = api.get_stream_url(get_id(r))
 except CallFailure as e:
     print('Error getting track URL from google.')
     continue
 logging.debug('URL = %s', url)
 while True:
     try:
         g = get(url, stream = True)
         if g.status_code != 200:
             logging.critical('Download failed with status code %s.', g.status_code)
             break
         else:
             g.raw.decode_content = True
             with open(filename, 'wb') as f:
                 copyfileobj(g.raw, f)
                 logging.debug('Wrote %s bytes.', len(g.content))
Example #40
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 #41
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 #42
0
def main():
    global player
    global api

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

    (opts, args) = parser.parse_args()

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

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

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

    id = 0

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

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

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

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

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

            if(opts.shuffle):
                shuffle(tracklist)

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

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

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

    finally:
        if isinstance(player, subprocess.Popen):
            player.terminate()
        api.logout()
Example #43
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 #44
0
class GooglePlayMusic(BaseModule):
	def __init__(self, client, options):
		super(GooglePlayMusic, self).__init__(client, options)

		self.api = Mobileclient()
		if self.api.login(self.options["username"], self.options["password"]):
			print "Login Successful"
		self.webClient = Webclient()
		self.webClient.login(self.options["username"], self.options["password"])

		#self.findRegisteredDevice()
		self.deviceId = self.options["deviceId"]
		self.speaker = self.options["speaker"]

		self.playing_track_list = False
		self.current_track_list = []
		self.current_track_index = 0

	"""
	def findRegisteredDevice(self):
		webClient = Webclient()
		webClient.login(self.options["username"], self.options["password"])
		registered_devices = webClient.get_registered_devices()
		for device in registered_devices:
			if device["type"] == "PHONE":
				self.deviceId = device["id"][2:] #removes the 0x from the front
	"""

	def onEvent(self, message):
		if message["from"] == self.speaker:
			if message["event"] == "streamEnded":
				if self.playing_track_list:
					self.continue_track_list()

	def onAction(self, message):
		if message["action"] == "search_and_play":
			query = message["data"]["query"]
			self.search_and_play(query)

		if message["action"] == "stopMusic" or message["action"] == "stop":
			self.stop_music()

		if message["action"] == "startRadio":
			query = message["data"]["query"]
			self.start_radio(query)

	def search_and_play(self, query):
		results = self.api.search_all_access(query)
		if not len(results["song_hits"]) > 0:
			return
		song_data = results["song_hits"][0]["track"]
		print song_data
		self.play_track(song_data)

	def play_track(self, song_data):
		print "Playing %s, by %s" % (song_data["title"], song_data["artist"])
		stream_url = self.api.get_stream_url(song_data["nid"], self.deviceId)
		self.client.emitAction(self.speaker, "streamMP3", {"url": stream_url})

	def stop_music(self):
		self.playing_track_list = False
		self.client.emitAction(self.speaker, "stopStreaming", {})

	def start_radio(self, query):
		results = self.api.search_all_access(query, max_results=10)
		top_song = results["song_hits"][0] if len(results["song_hits"]) else None
		top_album = results["album_hits"][0] if len(results["album_hits"]) else None
		top_artist = results["artist_hits"][0] if len(results["artist_hits"]) else None

		if not top_song:
			top_song = {"score": 0}
		if not top_album:
			top_album = {"score": 0}
		if not top_artist:
			top_artist = {"score": 0}

		station_id = None
		if top_song["score"] > top_album["score"]and top_song["score"] > top_artist["score"]:
			station_id = self.api.create_station(query, track_id=top_song["track"]["nid"])
		elif top_album["score"] > top_song["score"] and top_album["score"] > top_artist["score"]:
			station_id = self.api.create_station(query, album_id=top_album["album"]["albumId"])
		else:
			station_id = self.api.create_station(query, artist_id=top_artist["artist"]["artistId"])
		
		tracks = self.api.get_station_tracks(station_id)
		
		self.play_track_list(tracks)

	def play_track_list(self, tracks):
		self.playing_track_list = True
		self.current_track_list = tracks
		self.current_track_index = 0
		self.play_track(self.current_track_list[0])

	def continue_track_list(self):
		self.current_track_index += 1
		self.play_track(self.current_track_list[self.current_track_index])
Example #45
0
class IBCMusicClient():
    # Please have this be an absolute path
    SONG_DIR = "/home/pi/Desktop/JukeSite/songs"

    def __init__(self):
        self.api = None
        self.player = None

    def start(self):
        """
        Starts the MobileClient
        """
        self.api = Mobileclient()

    def stop(self):
        """
        Deletes MobileClient and sets self.api to default(None)
        """
        del self.api
        self.api = None

    def logon(self, email, password):
        """
        Logs onto google music as a mobile client. Returns true is sucessful.

        :param email: Email of the Google user
        :param password: Pass of the google user
        :return: Bool if connection was successful
        """
        if self.api is None:
            raise errors.MobileClientNotInitError(
                "The Client has not been init therefor it cannot logon.", 1000)

        try:
            res = self.api.login(email, password, self.api.FROM_MAC_ADDRESS)
        except AlreadyLoggedIn as e:
            self.api.logout()
            res = self.api.login(email, password, self.api.FROM_MAC_ADDRESS)

        del email
        del password
        return res

    def logout(self):
        """
        logs out of google music mobile client.

        :return: if it was succesful
        """
        return self.api.logout()

    def is_authenticated(self):
        if not self.api.is_authenticated():
            raise errors.SessionNotActive(
                "The session is no longer active. Either it timedout or you have not logged in",
                1001)

    def search_song(self, query):
        """
        Searchs for the given query and return the song results
        Will check for authentication.

        [{
            'track': {
                'album': 'Work Out',
                'albumArtRef': [{
                    'aspectRatio': '1',
                    'autogen': False,
                    'kind': 'sj#imageRef',
                    'url': 'http://lh5.ggpht.com/DVIg4GiD6msHfgPs_Vu_2eRxCyAoz0fFdxj5w...'
                }],
                'albumArtist': 'J.Cole',
                'albumAvailableForPurchase': True,
                'albumId': 'Bfp2tuhynyqppnp6zennhmf6w3y',
                'artist': 'J Cole',
                'artistId': ['Ajgnxme45wcqqv44vykrleifpji', 'Ampniqsqcwxk7btbgh5ycujij5i'],
                'composer': '',
                'discNumber': 1,
                'durationMillis': '234000',
                'estimatedSize': '9368582',
                'explicitType': '1',
                'genre': 'Pop',
                'kind': 'sj#track',
                'nid': 'Tq3nsmzeumhilpegkimjcnbr6aq',
                'primaryVideo': {
                    'id': '6PN78PS_QsM',
                    'kind': 'sj#video',
                    'thumbnails': [{
                        'height': 180,
                        'url': 'https://i.ytimg.com/vi/6PN78PS_QsM/mqdefault.jpg',
                        'width': 320
                    }]
                },
                'storeId': 'Tq3nsmzeumhilpegkimjcnbr6aq',
                'title': 'Work Out',
                'trackAvailableForPurchase': True,
                'trackAvailableForSubscription': True,
                'trackNumber': 1,
                'trackType': '7',
                'year': 2011
            },
            'type': '1'
        }]
        :param query: The song query
        :return: [list] all the song hits
        """
        self.is_authenticated()
        res = self.api.search(query)
        songs = res['song_hits']
        return songs

    def search_album(self, query):
        """
        Searchs for the given query and returns the album results.
        Will check for authenitcation.

        e.g return:
        [{
            'album': {
                'albumArtRef': 'http://lh5.ggpht.com/DVIg4GiD6msHfgPs_Vu_2eRxCyAoz0fF...',
                'albumArtist': 'J.Cole',
                'albumId': 'Bfp2tuhynyqppnp6zennhmf6w3y',
                'artist': 'J.Cole',
                'artistId': ['Ajgnxme45wcqqv44vykrleifpji'],
                'description_attribution': {
                    'kind': 'sj#attribution',
                    'license_title': 'Creative Commons Attribution CC-BY',
                    'license_url': 'http://creativecommons.org/licenses/by/4.0/legalcode',
                    'source_title': 'Freebase',
                    'source_url': ''
                },
                'explicitType': '1',
                'kind': 'sj#album',
                'name': 'Work Out',
                'year': 2011
            },
            'type': '3'
        }]

        :param query: [string] The album query
        :return: [list] A list of all the album hits
        """
        self.is_authenticated()
        res = self.api.search(query)
        albums = res['album_hits']
        return albums

    def get_album_info(self, album_id):
        """
        Returns information about an album

        e.g return:
        {
            'kind': 'sj#album',
            'name': 'Circle',
            'artist': 'Amorphis',
            'albumArtRef': 'http://lh6.ggpht.com/...',
            'tracks': [  # if `include_tracks` is True
            {
                'album': 'Circle',
                'kind': 'sj#track',
                'storeId': 'T5zb7luo2vkroozmj57g2nljdsy',  # can be used as a song id
                'artist': 'Amorphis',
                'albumArtRef': [
                {
                    'url': 'http://lh6.ggpht.com/...'
                }],
                'title': 'Shades of Grey',
                'nid': 'T5zb7luo2vkroozmj57g2nljdsy',
                'estimatedSize': '13115591',
                'albumId': 'Bfr2onjv7g7tm4rzosewnnwxxyy',
                'artistId': ['Apoecs6off3y6k4h5nvqqos4b5e'],
                'albumArtist': 'Amorphis',
                'durationMillis': '327000',
                'composer': '',
                'genre': 'Metal',
                'trackNumber': 1,
                'discNumber': 1,
                'trackAvailableForPurchase': True,
                'trackType': '7',
                'albumAvailableForPurchase': True
            }, # ...
            ],
            'albumId': 'Bfr2onjv7g7tm4rzosewnnwxxyy',
            'artistId': ['Apoecs6off3y6k4h5nvqqos4b5e'],
            'albumArtist': 'Amorphis',
            'year': 2013
        }

        :param album_id: The albumId
        :return: Dictionary in the format above
        """
        self.is_authenticated()
        return self.api.get_album_info(album_id)

    def get_song_info(self, song_id):
        """
        Returns information about a song

        e.g return
        {
            'album': 'Best Of',
            'kind': 'sj#track',
            'storeId': 'Te2qokfjmhqxw4bnkswbfphzs4m',
            'artist': 'Amorphis',
            'albumArtRef': [
            {
                'url': 'http://lh5.ggpht.com/...'
            }],
            'title': 'Hopeless Days',
            'nid': 'Te2qokfjmhqxw4bnkswbfphzs4m',
            'estimatedSize': '12325643',
            'albumId': 'Bsbjjc24a5xutbutvbvg3h4y2k4',
            'artistId': ['Apoecs6off3y6k4h5nvqqos4b5e'],
            'albumArtist': 'Amorphis',
            'durationMillis': '308000',
            'composer': '',
            'genre': 'Metal',
            'trackNumber': 2,
            'discNumber': 1,
            'trackAvailableForPurchase': True,
            'trackType': '7',
            'albumAvailableForPurchase': True
        }

        :param song_id: The songds storeId
        :return: A dict with the above information
        """
        self.is_authenticated()
        return self.api.get_track_info(song_id)

    def get_song_url(self, song_id):
        self.is_authenticated()
        res = self.api.get_stream_url(song_id)
        return res

    def download_song(self, song_id):
        """
        Download the song from the storeId.

        :param song_id: the 'storeId' of the specific song
        """
        url = self.get_song_url(song_id)
        song_file_path = "{}/{}.mp3".format(IBCMusicClient.SONG_DIR, song_id)
        if os.path.isfile(song_file_path):
            raise errors.SongAlreadyDownloadedException(
                "The song '{}' has already been downloaded and cached".format(
                    song_file_path), 8002)
        # This need to not use subprocessing
        command = ['wget', url, '-O', song_file_path]
        res = check_output(command)
        lines = res.decode().split('\n')
        error_lines = [line for line in lines if 'failed' in line]

        if len(error_lines) > 0:
            # We have an error
            raise errors.CannotDownloadSongError(
                "Could not download the given song. {}".format(
                    str(error_lines)), 1003)
Example #46
0
class GMusicHandler:
    """
    GMusicHandler is the class handling the communication with the google music api.
    """
    def __init__(self, user, password):
        self.user = user
        self.password = password

        self.api = Mobileclient()

        if not self.api.oauth_login(Mobileclient.FROM_MAC_ADDRESS):
            raise Exception('Failed to login...')

        self.tracks = {}
        self.playlists = {}

    def get_all_song(self):
        if len(self.tracks) == 0:
            songs = self.api.get_all_songs()
            for song in songs:
                track_id = song['id']
                title = song['title']
                artist = song['artist']
                album = song['album']
                track_number = song['trackNumber']

                self.tracks[track_id] = Track(track_id, title, artist, album,
                                              track_number)
        return self.tracks

    def get_song(self, track_id):
        songs = self.get_all_song()
        return songs.get(track_id)

    def get_all_playlist(self):
        if len(self.playlists) == 0:
            playlists = self.api.get_all_user_playlist_contents()
            for pl in playlists:
                playlist = self._build_playlist(pl)
                self.playlists[playlist.id] = playlist
        return self.playlists

    def _build_playlist(self, data):
        playlist_id = data['id']
        playlist_name = data['name']
        p = Playlist(playlist_id, playlist_name)
        for track in data['tracks']:
            track_id = track['trackId']
            source = track['source']
            if source == '1':
                song = self.get_song(track_id)
                p.tracks[song.id] = song
            elif source == '2':
                song = self._create_track(track_id, track['track'])
                p.tracks[song.id] = song

        return p

    def _create_track(self, track_id, data):
        title = data['title']
        artist = data['artist']
        album = data['album']
        track_number = data['trackNumber']
        song = Track(track_id, title, artist, album, track_number)
        self.tracks[song.id] = song
        return song

    def get_playlist(self, playlist_name):
        playlists = self.get_all_playlist()
        return next(
            iter([p for p in playlists.values() if p.name == playlist_name]),
            None)

    def download_track(self, track_id):
        url = self.api.get_stream_url(track_id)
        file_path, headers = urllib.request.urlretrieve(url)
        return file_path

    def search(self, query):
        return self.api.search(query)
for i in range(len(library)):
    sleep(0.2)

    filename = '{}{}{}{}'.format(path, library[i].get('artist'), '-',
                                 library[i].get('title'))
    songname = library[i].get('artist') + '-' + library[i].get('title')
    p_iter = p_iter + 100 / len(library)

    try:
        if os.path.exists(filename) == True:
            print('\n File {} already exist'.format(songname))
            bar.update(p_iter)
        else:
            try:
                url = api.get_stream_url(library[i].get('storeId'))
                print('\n Downloading {}'.format(songname))
                urllib.request.urlretrieve(url, filename)
                bar.update(p_iter)
            except ValueError:
                bar.update(p_iter)
                pass

    except HTTPError as e:
        if e.response.status_code == 403 or 503:
            sleep(1)
            print('\n Downloading {}'.format(songname))
            urllib.request.urlretrieve(url, filename)
            bar.update(p_iter)

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

        return ret

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

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

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

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

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

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

        tr = [_parse_track(i['track']) for i in data['song_hits']]
        ar = [_parse_artist(i['artist']) for i in data['artist_hits']]
        al = [_parse_album(i['album']) for i in data['album_hits']]
        #self.dmpdata("Search playlists", data['playlist_hits'])
        try:
            pl = [_parse_splaylist(i) for i in data['playlist_hits']]
        except:
            pl = []
        return SearchResult(artists=ar, albums=al, playlists=pl, tracks=tr)
Example #49
0
song = {}
tids = []

for i,v in enumerate(playlist):
    tids.append(v['trackId'])

for i,v in enumerate(songs):
    if v['id'] not in tids:
	song = v

errSong = None
try:
	guid = song['id']
except:
	#library = api.get_all_songs()
	errSong = clusters[cluster][random.randint(0, len(clusters[cluster])-1)]
	guid = errSong['id']

#device = webapi.get_registered_devices()
#print device[0]['id'][2:]
#streamURL = api.get_stream_url(guid, device[0]['id'][2:])
streamURL = api.get_stream_url(guid, '320b3128904ea650')
if errSong is None:
    #pid = api.create_playlist("Tempo: " + dt.datetime.now().strftime("%m-%d-%Y %I:%M:%S%p"))
    api.add_songs_to_playlist(pid, guid)
    print json.dumps({"streamURL": streamURL, "song": song}, indent=4, separators=(',', ': '))
else:
    api.add_songs_to_playlist(pid, guid)
    print json.dumps({"streamURL": streamURL, "song": errSong, "error": "true"}, indent=4, separators=(',', ': '))
Example #50
0
File: url.py Project: abauer/chorus
import sys
from gmusicapi import Mobileclient

if __name__ == "__main__":
    if sys.argv[1] == "1":
        mc = Mobileclient()
        success = mc.login(sys.argv[3], sys.argv[4])
        if success == True:
            url = mc.get_stream_url(sys.argv[2],
                                    sys.argv[5])  #songid, deviceid
            print(url)
    if sys.argv[1] == "2":
        print("Spotify is not yet supported.")
Example #51
0
password = os.environ.get( 'GMUSIC_PASSWORD' )

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

tracks = api.get_station_tracks('44b0d91f-1696-303b-ade0-3f67767a47da', 1)

track_urls = []

class Track(object):
    id = ""
    title = ""
    artist = ""
    url = ""

    def __init__(self, id, title, artist, url):
        self.id = id
        self.title = title
        self.artist = artist
        self.url = url

for track in tracks:
    track_id = track.get('storeId').encode('utf8')
    track_title = track.get('title').encode('utf8')
    track_artist = track.get('artist').encode('utf8')
    track_url = api.get_stream_url(track_id)

    new_track = Track(track_id, track_title, track_artist, track_url)

print json.dumps(new_track.__dict__)
Example #52
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 #53
0
class GoogleMusicLogin():
    def __init__(self):
        import requests
        from requests.packages.urllib3.exceptions import InsecureRequestWarning
        from requests.packages.urllib3.exceptions import InsecurePlatformWarning
        requests.packages.urllib3.disable_warnings(InsecureRequestWarning)
        requests.packages.urllib3.disable_warnings(InsecurePlatformWarning)
        self.gmusicapi = Mobileclient(debug_logging=False, validate=False, verify_ssl=False)

    def checkCookie(self):
        # Remove cookie data if it is older then 7 days
        if utils.addon.getSetting('cookie-date') != None and len(utils.addon.getSetting('cookie-date')) > 0:
            import time
            if (datetime.now() - datetime(*time.strptime(utils.addon.getSetting('cookie-date'), '%Y-%m-%d %H:%M:%S.%f')[0:6])).days >= 7:
                self.clearCookie()

    def checkCredentials(self):
        if not utils.addon.getSetting('username'):
            utils.addon.openSettings()
        if utils.addon.getSetting('password') and utils.addon.getSetting('password') != '**encoded**':
            import base64
            utils.addon.setSetting('encpassword',base64.b64encode(utils.addon.getSetting('password')))
            utils.addon.setSetting('password','**encoded**')

    def getApi(self):
        return self.gmusicapi

    def getStreamUrl(self,song_id):
        # retrieve registered device
        device_id = self.getDevice()
        # retrieve stream quality from settings
        quality = { '0':'hi','1':'med','2':'low' } [utils.addon.getSetting('quality')]
        utils.log("getStreamUrl songid: %s device: %s quality: %s"%(song_id, device_id, quality))

        return self.gmusicapi.get_stream_url(song_id, device_id, quality)

    def getDevice(self):
        return utils.addon.getSetting('device_id')

    def initDevice(self):
        device_id = self.getDevice()

        if not device_id:
            utils.log('Trying to fetch the device_id')
            self.login(True)
            try:
                devices = self.gmusicapi.get_registered_devices()
                if len(devices) == 10:
                    utils.log("WARNING: 10 devices already registered!")
                utils.log(repr(devices))
                for device in devices:
                    if device["type"] in ("ANDROID","PHONE","IOS"):
                        device_id = str(device["id"])
                        break
            except:
                pass

            if device_id:
                if device_id.lower().startswith('0x'): device_id = device_id[2:]
                utils.addon.setSetting('device_id', device_id)
                utils.log('Found device_id: '+device_id)
            else:
                #utils.log('No device found, using default.')
                #device_id = "333c60412226c96f"
                raise Exception('No devices found, registered mobile device required!')

    def clearCookie(self):
        utils.addon.setSetting('logged_in-mobile', "")
        utils.addon.setSetting('authtoken-mobile', "")
        utils.addon.setSetting('device_id', "")

    def logout(self):
        self.gmusicapi.logout()

    def login(self, nocache=False):
        if not utils.addon.getSetting('logged_in-mobile') or nocache:
            import base64

            utils.log('Logging in')
            self.checkCredentials()
            username = utils.addon.getSetting('username')
            password = base64.b64decode(utils.addon.getSetting('encpassword'))

            try:
                self.gmusicapi.login(username, password, utils.addon.getSetting('device_id'))
                if not self.gmusicapi.is_authenticated():
                    self.gmusicapi.login(username, password, Mobileclient.FROM_MAC_ADDRESS)
            except Exception as e:
                utils.log(repr(e))

            if not self.gmusicapi.is_authenticated():
                utils.log("Login failed")
                utils.addon.setSetting('logged_in-mobile', "")
                self.language = utils.addon.getLocalizedString
                dialog = xbmcgui.Dialog()
                dialog.ok(self.language(30101), self.language(30102))
                #utils.addon.openSettings()
                raise
            else:
                utils.log("Login succeeded")
                utils.addon.setSetting('logged_in-mobile', "1")
                utils.addon.setSetting('authtoken-mobile', self.gmusicapi.session._authtoken)
                utils.addon.setSetting('cookie-date', str(datetime.now()))
        else:

            utils.log("Loading auth from cache")
            self.gmusicapi.session._authtoken = utils.addon.getSetting('authtoken-mobile')
            self.gmusicapi.session.is_authenticated = True
Example #54
0
 path = os.path.join(args.dir, artist, album)
 filename = os.path.join(path, filename)
 if os.path.isfile(filename):
  print('Track already downloaded: %s - %s.' % (artist, title))
  continue
 if not os.path.isdir(path):
  logging.debug('Creating directory for downloaded track: %s', path)
  os.makedirs(path)
 if args.wait:
  print('Waiting %.2f seconds between tracks.' % args.wait)
  sleep(args.wait)
 print(u'[%s] Downloading %s - %s to %s.' % (i, artist, title, filename))
 start = time()
 logging.debug('Started download at %s.', ctime(start))
 try:
  url = api.get_stream_url(get_id(r))
 except CallFailure as e:
  print('Error getting track URL from google.')
  continue
 logging.debug('URL = %s', url)
 while True:
  try:
   g = get(url, stream = True)
   if g.status_code != 200:
    logging.critical('Download failed with status code %s.', g.status_code)
    break
   else:
    g.raw.decode_content = True
    with open(filename, 'wb') as f:
     copyfileobj(g.raw, f)
     logging.debug('Wrote %s bytes.', len(g.content))
Example #55
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 #56
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 #57
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 #58
0
class Downloader:

	def __init__( self , username1=None ,  password1=None , baseDirectory=None , pickleLIBFilePath=None ):

		self.api = Mobileclient()
		if username1 is not None:
			if password1 is not None:
				self.login( username1 , password1 )

		if self.isLoggedIn() == True:
			print("Logged In")
		else:
			raise Exception


		# Setup Default Save Location
		if baseDirectory is not None:
			self.homeDIR = baseDirectory
			self.libDIR = os.path.join( self.homeDIR , 'GMusicLocalLibraryPOOL' )
		else:
			self.homeDIR = os.path.expanduser("~")
			self.libDIR = os.path.join( self.homeDIR , 'GMusicLocalLibraryPOOL' )
		

		if not os.path.exists(self.libDIR):
			os.makedirs(self.libDIR)


		self.stations = {}
		self.workingPlaylistOBJ = {}
		self.needToDownloadSongs = None	

		self.Full = True

		#self.playlists = None
		self.localLibrary = None
		#self.initializePlaylists()
		if pickleLIBFilePath is not None:
			self.initializeLocalLibrary(pickleLIBFilePath)
		else:
			self.initializeLocalLibrary()


	def isLoggedIn(self):

		x = self.api.is_authenticated()
		return x

	def login(self , userN , passW ):

		self.api.login( userN , passW , Mobileclient.FROM_MAC_ADDRESS )

	def initializePlaylists(self):

		try:
			self.playlists = pickle.load( open( libDIR + "libPlaylists.p" , "rb" ) )
		except:
			print("Recreating Playlists Save File")
			self.playlists = {}
			playlists['EDM'] = []
			playlists['Relaxing'] = []
			playlists['EDM'].append('4b40425b-2e11-388f-aeed-ea736b88662c')
			pickle.dump( playlists , open( libDIR + "libPlaylists.p" , "wb" ) )

	def initializeLocalLibrary(self , pickleLIBFilePath=None):

		defaultPath2 = os.path.join( self.libDIR , "libDatabasePOOL.p" )
		
		if pickleLIBFilePath is not None:
			defaultPath2 = pickleLIBFilePath

		print("DefaultPath .p file = " + defaultPath2)
		
		try:
			self.localLibrary = pickle.load( open( defaultPath2  , "rb" ) )
			print("Loaded libDatabasePOOL.p")
		except:
			self.localLibrary = {}
			pickle.dump( self.localLibrary , open( defaultPath2 , "wb" ) )
			print("Recreated LibraryPOOL Save File")

		print( "LocalLibary Size = " + str( len( self.localLibrary ) ) )

	def getMyStations(self):

		stations = self.api.get_all_stations()
		for x in stations:
			self.stations[x['id']] = x['name']

	def printAvailableStations(self):

		for x in self.stations:
			print( str(x) + " = " + self.stations[x] )

	def downloadStationToPOOL( self , stationID ):

		rawPlaylist = self.api.get_station_tracks( stationID , 25 )

		self.needToDownloadSongs = {}

		for x in rawPlaylist:
			if x['nid'] in self.localLibrary:
				print("Already in LibraryPOOL")
			else:
				self.Full = False
				print( str(x['nid']) + " == Not in library ... need to download" )
				self.needToDownloadSongs[x['nid']] = { 'stationID': stationID , 'trackName': x['title'] , 'artistName': x['artist'] , 'albumID': x['albumId'] , 'artURL': x['albumArtRef'][0]['url'] }
				

		p1 = threading.Thread( target=self.getMP3FromSongIDS , args=( stationID , ) )
		p1.start()
		p1.join()

		if self.Full == False:
			self.Full = True
			print( "LocalLibary Size = " + str( len( self.localLibrary ) ) )
			self.downloadStationToPOOL( stationID )

	def getMP3FromSongIDS( self , stationID ):
		
		a1 = 1
		for x in self.needToDownloadSongs:

			self.saveMP3ToLibraryPOOL( x , self.needToDownloadSongs[x]['trackName'] , self.needToDownloadSongs[x]['artistName'] , stationID )
			self.localLibrary[x] = self.needToDownloadSongs[x]
			pickle.dump( self.localLibrary , open( os.path.join( self.libDIR , "libDatabasePOOL.p" ) , "wb" ) )
			print("added [" + str(a1) + " of " + str(len(self.needToDownloadSongs)) + "] songs to localLibrary")
			a1 = a1 + 1
			
	def saveMP3ToLibraryPOOL( self , songID , name , artist , stationID ):

		albumName = self.stations[stationID]
		
		wURL = self.api.get_stream_url( songID , self.api.android_id , 'hi' )
		fN = os.path.join( self.libDIR , songID + ".mp3" )

		response1 = requests.get( wURL , stream=True )
		with open( fN , 'wb' ) as f:
			for data in tqdm( response1.iter_content(chunk_size=524288) ):
				f.write(data)		


		m3 = MP3( fN , ID3=EasyID3 )
		m3.add_tags( ID3=EasyID3 )
		m3["title"] = name
		m3['artist'] = artist
		m3['album'] = albumName
		m3['organization'] = stationID 
		m3.save()

	def extractSinglePlaylistFromPOOL( self , stationID , destinationDIR , onlyCopyNotExtract=False ):

		if not os.path.exists(destinationDIR):
			os.makedirs(destinationDIR)

		if onlyCopyNotExtract == True:
			for key , value in self.localLibrary.items():
				try:
					if value['stationID'] == stationID:
						#print( "found --> " + self.localLibrary[key]['trackName'] + " in ... " + str(stationID) )
						fN = str(key) + ".mp3" 
						shutil.copy( os.path.join( self.libDIR , fN ) , os.path.join( destinationDIR , fN ) )
				except:
					pass		

		else:
			for key , value in self.localLibrary.items():
				try:
					if value['stationID'] == stationID:
						#print( "found --> " + self.localLibrary[key]['trackName'] + " in ... " + str(stationID) )
						fN = str(key) + ".mp3" 
						shutil.move( os.path.join( self.libDIR , fN ) , os.path.join( destinationDIR , fN ) )
				except:
					pass