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)
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
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))