Exemplo n.º 1
0
class TestAudioServiceControls(TestCase):
    def assertLastMessageTypeEqual(self, bus, msg_type):
        message = bus.emit.call_args_list[-1][0][0]
        self.assertEqual(message.msg_type, msg_type)

    def setUp(self):
        self.bus = mock.Mock(name='bus')
        self.audioservice = AudioService(self.bus)

    def test_pause(self):
        self.audioservice.pause()
        self.assertLastMessageTypeEqual(self.bus,
                                        'mycroft.audio.service.pause')

    def test_resume(self):
        self.audioservice.resume()
        self.assertLastMessageTypeEqual(self.bus,
                                        'mycroft.audio.service.resume')

    def test_next(self):
        self.audioservice.next()
        self.assertLastMessageTypeEqual(self.bus, 'mycroft.audio.service.next')

    def test_prev(self):
        self.audioservice.prev()
        self.assertLastMessageTypeEqual(self.bus, 'mycroft.audio.service.prev')

    def test_stop(self):
        self.audioservice.stop()
        self.assertLastMessageTypeEqual(self.bus, 'mycroft.audio.service.stop')

    def test_seek(self):
        self.audioservice.seek()
        message = self.bus.emit.call_args_list[-1][0][0]
        self.assertEqual(message.msg_type,
                         'mycroft.audio.service.seek_forward')
        self.assertEqual(message.data['seconds'], 1)
        self.audioservice.seek(5)
        message = self.bus.emit.call_args_list[-1][0][0]
        self.assertEqual(message.msg_type,
                         'mycroft.audio.service.seek_forward')
        self.assertEqual(message.data['seconds'], 5)
        self.audioservice.seek(-5)
        message = self.bus.emit.call_args_list[-1][0][0]
        self.assertEqual(message.msg_type,
                         'mycroft.audio.service.seek_backward')
        self.assertEqual(message.data['seconds'], 5)
Exemplo n.º 2
0
class Deezer(CommonPlaySkill):
    def __init__(self):
        super(Deezer, self).__init__()
        self.regexes = {}
        self.playlist_data = None
        self.playing_wait_thread = None
        self.playing_thread = None
        self.playlist_playing_index = Value('i', -1)
        self.playing_seconds = Value('i', -1)

    def initialize(self):
        super().initialize()
        self.audio_service = AudioService(self.bus)
        self.add_event('mycroft.audio.service.next', self.next_track)
        self.add_event('mycroft.audio.service.prev', self.prev_track)
        self.add_event('mycroft.audio.service.pause', self.pause)
        self.add_event('mycroft.audio.service.resume', self.resume)
        self.arl = self.settings.get('arl')
        # TODO directory should probably default to self.file_system.path
        # This is a unique directory for each Skill.
        # There's also mycroft.util.get_cache_directory if you intend it to be temporary
        self.music_dir = self.settings.get('music_dir', self.file_system.path)
        self.track_directory = os.path.join(self.music_dir, "track")

    def CPS_match_query_phrase(self, phrase):
        self.log.info(f"Check Query Phrase: {phrase}")

        phrase, cps_match_level, data = self.specific_query(phrase=phrase)
        if cps_match_level is None:
            track = deezer_utils.search_first_track(track_name=phrase,
                                                    arl=self.arl)
            if track is None:
                return None
            else:
                track_id = track.get('id')
                self.speak_dialog(key="track_found",
                                  data={
                                      'title_short': track["title_short"],
                                      'artist': track['artist']['name']
                                  })
                download_path = deezer_utils.download_track(
                    track_id=track_id,
                    track_directory=self.track_directory,
                    arl=self.arl)
                data = {
                    "type": 0,
                    "track": download_path,
                    "track_id": track_id
                }
                if 'deezer' in phrase:
                    cps_match_level = CPSMatchLevel.EXACT
                else:
                    cps_match_level = CPSMatchLevel.TITLE

        return phrase, cps_match_level, data

    """ This method responds wether the skill can play the input phrase.

        The method is invoked by the PlayBackControlSkill.

        Returns: tuple (matched phrase(str),
                        match level(CPSMatchLevel),
                        optional data(dict))
                 or None if no match was found.
    """

    def CPS_start(self, phrase, data):
        if self.playing_thread is not None:
            self.playing_thread.kill()
            self.playing_thread = None
        if self.playlist_data is not None:
            self.playlist_data = None
        if self.playlist_playing_index.value is not None:
            self.playlist_playing_index.value = -1

        if data['type'] == 0:
            self.log.info("TrackType is Track")
            self.CPS_play(data['track'])
        elif data['type'] == 1:
            self.log.info("TrackType is Playlist")
            playlist = data['playlist']
            self.playlist_data = data
            playlist_search_results = data['playlist_search_results']
            track_directory = os.path.join(self.music_dir,
                                           str(playlist_search_results['id']))
            self.playing_thread = Process(target=self.playing_playlist,
                                          args=(playlist, track_directory, 0,
                                                -1))
            self.playing_thread.start()
            self.playing_thread.join()
            shutil.rmtree(track_directory, ignore_errors=True)

    """ Starts playback.
    
        Called by the playback control skill to start playback if the
        skill is selected (has the best match level)
    """

    def specific_query(self, phrase):
        # Check if saved
        # match = re.match(self.translate_regex('saved_songs'), phrase)
        # if match and self.saved_tracks:
        #     return (1.0, {'data': None,
        #                   'type': 'saved_tracks'})

        # Check if playlist
        phrase = phrase.lower()
        match = re.match(self.translate_regex('playlist'), phrase)
        if match:
            playlist_search_results = deezer_utils.search_first_playlist(
                match.groupdict()['playlist'], self.arl)
            if playlist_search_results:
                tracklist = requests.get(
                    playlist_search_results['tracklist']).json()
                try:
                    data = tracklist["data"]
                    next_tracklist_url = tracklist['next']
                    try:
                        while True:
                            next_tracklist = requests.get(
                                next_tracklist_url).json()
                            data += next_tracklist['data']
                            next_tracklist_url = next_tracklist['next']
                            self.log.info(next_tracklist_url)
                    except KeyError as index:
                        pass
                except KeyError as dataError:
                    pass
                return_data = {
                    'type': 1,
                    'playlist': data,
                    'playlist_search_results': playlist_search_results
                }
                return phrase, CPSMatchLevel.TITLE, return_data
            else:
                return phrase, CPSMatchLevel.GENERIC, None
        # Check album
        # match = re.match(self.translate_regex('album'), phrase)
        # if match:
        #     album = match.groupdict()['album']
        #     return self.query_album(album)
        #
        # # Check artist
        # match = re.match(self.translate_regex('artist'), phrase)
        # if match:
        #     artist = match.groupdict()['artist']
        #     return self.query_artist(artist)
        # match = re.match(self.translate_regex('song'), phrase)
        # if match:
        #     song = match.groupdict()['track']
        #     return self.query_song(song)

        return phrase, None, None

    def playing_playlist(self, playlist, track_directory, start_index, seek):
        for i in range(start_index, len(playlist)):
            try:
                self.playlist_playing_index.value = i
                track_id = playlist[i]['id']
                downloaded_track = deezer_utils.download_track(
                    track_id=track_id,
                    track_directory=track_directory,
                    arl=self.arl)

                self.log.info(str(downloaded_track))
                if seek > -1:
                    self.audio_service.seek(seconds=seek)
                    self.audio_service.resume()
                else:
                    self.CPS_play(downloaded_track)
                self.log.info("Playing now ...")
                duration = playlist[i]['duration']
                for d in range(0, duration):
                    self.playing_seconds.value = d
                    time.sleep(1)

                shutil.rmtree(downloaded_track, ignore_errors=True)
            except Exception as e:
                print(e)
                self.log.error(e)

    def next_track(self):
        if self.playlist_data is not None:
            if self.playing_thread is not None:
                self.playing_thread.kill()
                self.playing_thread = None
            playlist_search_results = self.playlist_data[
                'playlist_search_results']
            track_directory = os.path.join(self.music_dir,
                                           str(playlist_search_results['id']))
            if self.playlist_playing_index.value + 1 >= len(
                    self.playlist_data['playlist']):
                self.speak_dialog(
                    key='playlist.end',
                    data={
                        'title':
                        self.playlist_data['playlist_search_results']['title']
                    })
                self.playlist_data = None
                self.playlist_playing_index.value = -1
                shutil.rmtree(track_directory)
                return
            self.playing_thread = Process(
                target=self.playing_playlist,
                args=(self.playlist_data['playlist'], track_directory,
                      self.playlist_playing_index.value + 1, -1))
            self.playing_thread.start()
            self.playing_thread.join()

    def prev_track(self):
        if self.playlist_data is not None:
            if self.playing_thread is not None:
                self.playing_thread.kill()
                self.playing_thread = None
            playlist_search_results = self.playlist_data[
                'playlist_search_results']
            track_directory = os.path.join(self.music_dir,
                                           str(playlist_search_results['id']))
            if self.playlist_playing_index.value + 1 >= len(
                    self.playlist_data['playlist']):
                self.speak_dialog(
                    key='playlist.end',
                    data={
                        'title':
                        self.playlist_data['playlist_search_results']['title']
                    })
                self.playlist_data = None
                self.playlist_playing_index.value = -1
                shutil.rmtree(track_directory)
                return
            index = self.playlist_playing_index.value - 1
            if index < 0:
                index = index + 1

            self.playing_thread = Process(target=self.playing_playlist,
                                          args=(self.playlist_data['playlist'],
                                                track_directory, index, -1))
            self.playing_thread.start()
            self.playing_thread.join()

    def pause(self):
        if self.playlist_data is not None:
            if self.playing_thread is not None:
                self.playing_thread.kill()
                self.playing_thread = None

            self.audio_service.pause()

    def resume(self):
        if self.playlist_data is not None:
            if self.playing_thread is not None:
                self.playing_thread.kill()
                self.playing_thread = None
            playlist_search_results = self.playlist_data[
                'playlist_search_results']
            track_directory = os.path.join(self.music_dir,
                                           str(playlist_search_results['id']))

            self.playing_thread = Process(
                target=self.playing_playlist,
                args=(self.playlist_data['playlist'], track_directory,
                      self.playlist_playing_index.value,
                      self.playing_seconds.value))
            self.playing_thread.start()
            self.playing_thread.join()
        pass

    def translate_regex(self, regex):
        if regex not in self.regexes:
            path = self.find_resource(regex + '.regex')
            if path:
                with open(path) as f:
                    string = f.read().strip()
                self.regexes[regex] = string
        return self.regexes[regex]

    @intent_handler('user.intent')
    def speak_user_name(self, message):
        self.log.info("Username Intent")
        self.speak_dialog(
            key='user',
            data={'user_name': deezer_utils.get_user_info(arl=self.arl)})