Example #1
0
    def test_available_backends(self):
        bus = mock.Mock(name='bus')
        audioservice = AudioService(bus)

        available_backends = {
            'simple': {
                'suported_uris': ['http', 'file'],
                'default': True,
                'remote': False
            }
        }
        bus.wait_for_response.return_value = Message('test_msg',
                                                     available_backends)
        response = audioservice.available_backends()
        self.assertEqual(available_backends, response)
        # Check no response behaviour
        bus.wait_for_response.return_value = None
        response = audioservice.available_backends()
        self.assertEqual({}, response)
Example #2
0
class Jellyfin(CommonPlaySkill):

    def __init__(self):
        super().__init__()
        self._setup = False
        self.audio_service = None
        self.jellyfin_croft = None
        self.songs = []
        self.device_id = hashlib.md5(
            ('Jellyfin'+DeviceApi().identity.uuid).encode())\
            .hexdigest()

    def CPS_match_query_phrase(self, phrase):
        """ This method responds whether 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.
        """
        # slower devices like raspberry pi's need a bit more time.
        self.CPS_extend_timeout(10)
        # first thing is connect to jellyfin or bail
        if not self.connect_to_jellyfin():
            return None

        self.log.debug("CPS Phrase: " + phrase)
        match_type, self.songs = self.jellyfin_croft.parse_common_phrase(phrase)

        if match_type and self.songs:
            match_level = None
            if match_type != None:
                self.log.info('Found match of type: ' + match_type)

                if match_type == 'song' or match_type == 'album' or match_type == 'playlist' or match_type == 'genre':
                    match_level = CPSMatchLevel.TITLE
                elif match_type == 'artist':
                    match_level = CPSMatchLevel.ARTIST
                self.log.info('match level :' + str(match_level))
    
            song_data = dict()
            song_data[phrase] = self.songs
            
            self.log.info("First 3 item urls returned")
            max_songs_to_log = 3
            songs_logged = 0

            for song in self.songs:
                self.log.debug(song)
                songs_logged = songs_logged + 1
                if songs_logged >= max_songs_to_log:
                    break
            return phrase, CPSMatchLevel.TITLE, song_data
        else:
            return None

    def CPS_start(self, phrase, data):
        """ Starts playback.

            Called by the playback control skill to start playback if the
            skill is selected (has the best match level)
        """    
        # setup audio service
        self.audio_service = AudioService(self.bus)
        self.speak_playing(phrase)
        self.audio_service.play(data[phrase])
        self.CPS_send_tracklist(self.jellyfin_croft.get_track_list())

    def connect_to_jellyfin(self, diagnostic=False):
        """
        Attempts to connect to the server based on the config
        if diagnostic is False an attempt to auth is also made
        returns true/false on success/failure respectively

        :return:
        """
        auth_success = False
        self.log.debug("Testing connection to: " + self.settings["hostname"])
        try:
            self.jellyfin_croft = JellyfinCroft(
                self.settings["hostname"] + ":" + str(self.settings["port"]),
                self.settings["username"], self.settings["password"],
                self.device_id, diagnostic)
            auth_success = True
        except Exception as e:
            self.log.info("failed to connect to jellyfin, error: {0}".format(str(e)))

        return auth_success

    def initialize(self):
        pass

    @intent_file_handler('jellyfin.intent')
    def handle_jellyfin(self, message):

        self.log.info(message.data)

        # first thing is connect to jellyfin or bail
        if not self.connect_to_jellyfin():
            self.speak_dialog('configuration_fail')
            return

        # determine intent
        intent, intent_type = JellyfinCroft.determine_intent(message.data)

        self.songs = []
        try:
            self.songs = self.jellyfin_croft.handle_intent(intent, intent_type)
        except Exception as e:
            self.log.info(e)
            self.speak_dialog('play_fail', {"media": intent})

        if not self.songs or len(self.songs) < 1:
            self.log.info('No songs Returned')
            self.speak_dialog('play_fail', {"media": intent})
        else:
            # setup audio service and play        
            self.audio_service = AudioService(self.bus)
            backends = self.audio_service.available_backends()
            self.log.debug("BACKENDS. VLC Recommended")
            for key , value in backends.items():
                self.log.debug(str(key) + " : " + str(value))
            self.speak_playing(intent)
            self.audio_service.play(self.songs, message.data['utterance'])

    @intent_file_handler('shuffle.intent')
    def handle_shuffle(self, message):
        self.log.info(message.data)
        # Back up meta data
        track_meta = self.jellyfin_croft.get_all_meta()
        # first thing is connect to jellyfin or bail
        if not self.connect_to_jellyfin():
            self.speak_dialog('configuration_fail')
            return

        if not self.songs or len(self.songs) < 1:
            self.log.info('No songs Returned')
            self.speak_dialog('shuffle_fail')
        else:
            self.log.info(track_meta)
            # setup audio service and, suffle play
            shuffle(self.songs)
            self.audio_service = AudioService(self.bus)
            self.speak_dialog('shuffle')
            self.audio_service.play(self.songs, message.data['utterance'])
            # Restore meta data
            self.jellyfin_croft.set_meta(track_meta)

    def speak_playing(self, media):
        data = dict()
        data['media'] = media
        self.speak_dialog('jellyfin', data)

    @intent_file_handler('playingsong.intent')
    def handle_playing(self, message):
        track = "Unknown"
        artist = "Unknown"
        if self.audio_service.is_playing:
            # See if I can get the current track index instead
            track = self.audio_service.track_info()['name']
            artist = self.audio_service.track_info()['artists']
            if artist != [None]:
                self.speak_dialog('whatsplaying', {'track' : track, 'artist': artist})
            else:
                track = self.jellyfin_croft.get_meta(self.audio_service.track_info()['name'])
                if track != False:
                    self.speak_dialog('whatsplaying', {'track' : track['Name'], 'artist': track['Artists']})
                else:
                    self.speak_dialog('notrackinfo')
        else:
            self.speak_dialog('notplaying')

    @intent_file_handler('playlist.intent')
    def handle_playlist_add(self, message):
        if self.audio_service.is_playing:
            track = self.audio_service.track_info()['name']
            track_name = self.jellyfin_croft.get_meta(track)
            add_to = self.jellyfin_croft.add_to_playlist(track, message.data.get('playlist_name'))
            if add_to == True:
                self.speak_dialog('playlist', {'media' : track_name['Name'], 'playlist_name' : message.data.get('playlist_name')})
                return
        self.speak_dialog('playlist_fail', {'media' : track_name['Name'], 'playlist_name' : message.data.get('playlist_name')})
        return

    @intent_file_handler('diagnostic.intent')
    def handle_diagnostic(self, message):

        self.log.info(message.data)
        self.speak_dialog('diag_start')

        # connec to jellyfin for diagnostics
        self.connect_to_jellyfin(diagnostic=True)
        connection_success, info = self.jellyfin_croft.diag_public_server_info()

        if connection_success:
            self.speak_dialog('diag_public_info_success', info)
        else:
            self.speak_dialog('diag_public_info_fail', {'host': self.settings['hostname']})
            self.speak_dialog('general_check_settings_logs')
            self.speak_dialog('diag_stop')
            return

        if not self.connect_to_jellyfin():
            self.speak_dialog('diag_auth_fail')
            self.speak_dialog('diag_stop')
            return
        else:
            self.speak_dialog('diag_auth_success')

        self.speak_dialog('diagnostic')

    def stop(self):
        pass
Example #3
0
class Mediaplayer(CommonPlaySkill):
    def __init__(self):
        #MycroftSkill.__init__(self)
        super().__init__(name="Mediaplayer")

    def initialize(self):
        super().initialize()
        self.playlists = []
        self.vlc_all_tracks = []
        self.vlc_all_tracks_info = []
        self.audio_service = AudioService(self.bus)
        self.vlc_audio_path = Path(str(self.settings.get('vlc_audio_path')))
        self.current_track = []
        self.track_change_request_in_progress = False
        #self.add_event("")
        self.register_all_intents()

    def register_all_intents(self):
        #self.register_intent_file('mediaplayer.next.intent', self.handle_mediaplayer_next)
        #self.register_intent_file('mediaplayer.prev.intent', self.handle_mediaplayer_prev)
        #self.register_intent_file('mediaplayer.stop.intent', self.handle_mediaplayer_stop)
        #self.register_intent_file('mediaplayer.pause.intent', self.handle_mediaplayer_pause)
        #self.register_intent_file('mediaplayer.resume.intent', self.handle_mediaplayer_resume)
        #self.register_intent_file('mediaplayer.info.intent', self.handle_mediaplayer_info)
        pass

    def enable_play_control_intents(self):
        self.enable_intent("mediaplayer.next.intent")
        self.enable_intent("mediaplayer.prev.intent")
        self.enable_intent("mediaplayer.stop.intent")
        self.enable_intent("mediaplayer.pause.intent")
        self.enable_intent("mediaplayer.resume.intent")

    def disable_play_controls(self):
        self.disable_intent("mediaplayer.next.intent")
        self.disable_intent("mediaplayer.prev.intent")
        self.disable_intent("mediaplayer.stop.intent")
        self.disable_intent("mediaplayer.pause.intent")
        self.disable_intent("mediaplayer.resume.intent")

    def handle_mediaplayer_info(self, message):
        self.speak_dialog('mediaplayer')
        self.speak("looking for backends.")
        for backend in self.audio_service.available_backends().keys():
            backend_text = "found " + str(backend)
            self.speak(backend_text)

    def handle_mediaplayer_next(self, message):
        if self.audio_service.is_playing:
            if not self.is_track_change_request_in_progress():
                self.play_next(message)
        else:
            self.speak("Nothing playing")

    def handle_mediaplayer_prev(self, message):
        if self.audio_service.is_playing:
            self.play_prev(message)
        else:
            self.speak("Nothing playing")

    def handle_mediaplayer_stop(self, message):
        if self.audio_service.is_playing:
            self.play_stop(message)
        else:
            self.speak("Nothing playing")

    def handle_mediaplayer_pause(self, message):
        self.speak("pause")
        self.play_pause(message)

    def handle_mediaplayer_resume(self, message):
        self.speak("resume")
        pass

    # @intent_handler('mediaplayer.play.intent')
    # def handle_mediaplayer_play(self, message):
    #     if not self.is_playing:
    #         self.play(message)
    #     else:
    #         self.speak("Already playing")

    def load_files_in_audio_path(self, path):
        tracks = []

        for dirpath, dirnames, filenames in os.walk(path):
            for file in filenames:
                track_path = Path(dirpath)
                track_path = track_path / file
                track_uri = 'file://' + str(track_path.resolve())
                track_data = (track_uri)
                tracks.append(track_data)
        return tracks

    def init_vlc_audio_list(self):
        self.vlc_all_tracks = self.load_files_in_audio_path(
            self.vlc_audio_path)
        # for track in self.vlc_all_tracks:
        #     self.audio_service.play(track)
        #     self.vlc_all_tracks_info.append([ track, self.audio_service.track_info()])
        #     self.audio_service.pause()

        #self.queue_tracks(self.vlc_all_tracks)
        self.current_track = []

    def get_vlc_track_info(self):
        pass

    def queue_tracks(self, tracks):
        self.audio_service.queue(tracks)

    def add_track_to_list(self, track, list):
        pass

    def add_tracks_to_list(self, tracks, list):
        pass

    def play(self, message):

        if not self.audio_service.is_playing:
            if not self.vlc_all_tracks:
                self.init_vlc_audio_list()
                self.audio_service.play(self.vlc_all_tracks, 'vlc')
                self.set_init_track()
            else:
                self.audio_service.play(self.vlc_all_tracks, 'vlc')
                self.set_init_track()
            #self.audio_service.play(self.audio_service.)
            #self.audio_service.play(self.vlc_all_tracks, 'vlc')
        pass

    def play_next(self, message):

        if self.audio_service.is_playing:
            self.audio_service.next()
            #if not self.is_track_change_request_in_progress():
            #    self.start_track_change_request()
            #    self.audio_service.next()
        pass

    def play_prev(self, message):
        pass

    def play_random(self, message):
        pass

    def play_resume(self, message):
        if self.audio_service.is_playing:
            self.audio_service.resume()

    def play_stop(self, message):
        if self.audio_service.is_playing:
            self.audio_service.pause()

    def play_pause(self, message):
        if self.audio_service.is_playing:
            self.audio_service.pause()

    def track_info(self, message):
        return self.audio_service.track_info()

    def track_info_reply(self, message):
        self.speak("request: track_info_reply")
        #return self.audio_service.track_info()

    def queue_track(self, message):
        self.speak("event: queue_track")
        pass

    def set_init_track(self):
        self.current_track = self.audio_service.track_info()

    def is_track_change_request_in_progress(self):
        if self.track_change_request_in_progress == True:
            if self.current_track != self.audio_service.track_info():
                self.complete_track_change_request()

        return self.track_change_request_in_progress

    def start_track_change_request(self):
        self.track_change_request_in_progress = True

    def complete_track_change_request(self):
        self.current_track = self.audio_service.track_info()
        self.track_change_request_in_progress = False

    def CPS_match_query_phrase(self, phrase):
        self.speak("phrase : " + str(phrase))

        if self.voc_match(phrase, "mediaplayer"):
            level = CPSMatchLevel.GENERIC
            phrase = "mediaplayer"
        else:
            level = CPSMatchLevel.GENERIC
        return (phrase, level)

    def CPS_start(self, phrase, data):
        data = {
            "phrase": phrase,
            "skill_id": self.skill_id,
            "service_name": self.spoken_name,
        }
        self.play(phrase)
        pass

    def CPS_send_status(self, artist='', track='', image=''):
        data = {
            'skill': self.name,
            'artist': artist,
            'track': track,
            'image': image,
            'status': None  # TODO Add status system
        }
        self.bus.emit(Message('play:status', data))