コード例 #1
0
ファイル: mdns.py プロジェクト: tb0hdan/voiceplay
 def start(self):
     """
     Start Zeroconf inside thread
     """
     self.threads = ThreadGroup()
     self.threads.targets = [self.run]
     self.threads.start_all()
コード例 #2
0
ファイル: argparser.py プロジェクト: tb0hdan/voiceplay
 def wakeword_loop():
     """
     Run wakeword listener loop
     """
     th = ThreadGroup()
     th.targets = [[subprocess.call, ['python', '-m', 'voiceplay.recognition.wakeword.sender']]]
     th.start_all()
     return th
コード例 #3
0
ファイル: tts.py プロジェクト: tb0hdan/voiceplay
 def start(self):
     """
     Start TTS as a separate thread
     """
     logger.debug('Starting TTS engine...')
     self.threads = ThreadGroup()
     self.threads.targets = [self.poll_loop]
     self.threads.start_all()
コード例 #4
0
 def start(self):
     """
     Start VickiPlayer and auxiliary loops using thread group
     """
     # non-blocking start
     self.player.start(debug=self.debug)
     self.thread_group = ThreadGroup()
     self.thread_group.targets = [
         self.task_loop, self.cmd_loop, self.prefetch_loop
     ]
     self.thread_group.start_all()
コード例 #5
0
ファイル: argparser.py プロジェクト: tb0hdan/voiceplay
 def wakeword_loop():
     """
     Run wakeword listener loop
     """
     th = ThreadGroup()
     th.targets = [[
         subprocess.call,
         ['python', '-m', 'voiceplay.recognition.wakeword.sender']
     ]]
     th.start_all()
     return th
コード例 #6
0
ファイル: argparser.py プロジェクト: tb0hdan/voiceplay
 def vicki_loop(vicki, noblock=False):
     """
     Run Vicki loop
     """
     vicki.player.start()
     vicki.tts.start()
     ThreadedRequestHandler.callback = vicki.wakeword_callback
     address = ('127.0.0.1', 63455)
     server = WakeWordReceiver(address, ThreadedRequestHandler)
     threads = ThreadGroup()
     threads.targets = [server.serve_forever]
     threads.start_all()
     vicki.run_forever_new(server, noblock=noblock)
コード例 #7
0
ファイル: argparser.py プロジェクト: tb0hdan/voiceplay
 def vicki_loop(vicki, noblock=False):
     """
     Run Vicki loop
     """
     vicki.player.start()
     vicki.tts.start()
     ThreadedRequestHandler.callback = vicki.wakeword_callback
     address = ('127.0.0.1', 63455)
     server = WakeWordReceiver(address,
                               ThreadedRequestHandler)
     threads = ThreadGroup()
     threads.targets = [server.serve_forever]
     threads.start_all()
     vicki.run_forever_new(server, noblock=noblock)
コード例 #8
0
ファイル: mdns.py プロジェクト: tb0hdan/voiceplay
 def start(self):
     """
     Start Zeroconf inside thread
     """
     self.threads = ThreadGroup()
     self.threads.targets = [self.run]
     self.threads.start_all()
コード例 #9
0
ファイル: vicki.py プロジェクト: tb0hdan/voiceplay
 def run_forever_new(self, wakeword_receiver, noblock=False):
     """
     Recognition main loop
     """
     self.wakeword_receiver = wakeword_receiver
     if noblock:
         threads = ThreadGroup()
         threads.targets = [self.background_listener]
         threads.start_all()
         return
     while not self.shutdown:
         try:
             self.background_listener()
         except (KeyboardInterrupt, SystemExit):
             self.stop()
         except Exception as exc:
             logger.debug('Background listener failed with %r, restarting...', exc)
コード例 #10
0
 def run_forever_new(self, wakeword_receiver, noblock=False):
     """
     Recognition main loop
     """
     self.wakeword_receiver = wakeword_receiver
     if noblock:
         threads = ThreadGroup()
         threads.targets = [self.background_listener]
         threads.start_all()
         return
     while not self.shutdown:
         try:
             self.background_listener()
         except (KeyboardInterrupt, SystemExit):
             self.stop()
         except Exception as exc:
             logger.debug(
                 'Background listener failed with %r, restarting...', exc)
コード例 #11
0
ファイル: vickiplayer.py プロジェクト: tb0hdan/voiceplay
 def start(self):
     """
     Start VickiPlayer and auxiliary loops using thread group
     """
     # non-blocking start
     self.player.start(debug=self.debug)
     self.thread_group = ThreadGroup()
     self.thread_group.targets = [self.task_loop, self.cmd_loop, self.prefetch_loop]
     self.thread_group.start_all()
コード例 #12
0
ファイル: argparser.py プロジェクト: tb0hdan/voiceplay
 def player_console(vicki, queue=None):
     """
     Start VickiPlayer console
     """
     helper = Help()
     console = Console()
     console.add_handler(Command.PLAY, vicki.player.play_from_parser,
                         Command().CONTROLS)
     console.add_handler('what', vicki.player.play_from_parser)
     console.add_handler('current_track', vicki.player.play_from_parser)
     helper.register(console)
     console.set_queue(queue)
     th = ThreadGroup(restart=False)
     th.targets = [console.run_bg_queue]
     th.start_all()
     console.run_console()
     console.set_exit()
     th.stop_all()
コード例 #13
0
 def wakeword_listener(self):
     """
     Wakeword listener
     """
     logger.warning('starting detector!')
     threads = ThreadGroup()
     threads.targets = [self.async_worker]
     threads.start_all()
     sensitivity = [0.5, 0.3, 0.3]  #[0.5] * len(self.models)
     self.detector = snowboydecoder.HotwordDetector(
         [os.path.join(self.basedir, model) for model in self.models],
         sensitivity=sensitivity,
         audio_gain=1)
     try:
         self.detector.start(detected_callback=self.wakeword_callback,
                             interrupt_check=self.interrupt_check,
                             sleep_time=0.03)
     except (KeyboardInterrupt, SystemExit):
         self.exit = True
         threads.stop_all()
コード例 #14
0
ファイル: voiceplay_osx.py プロジェクト: tb0hdan/voiceplay
 def player_console(self, vicki, queue=None):
     """
     Start VickiPlayer console
     """
     helper = Help()
     #self.console = Console()
     self.console.add_handler(Command.PLAY, vicki.player.play_from_parser, Command().CONTROLS)
     self.console.add_handler('what', vicki.player.play_from_parser)
     self.console.add_handler('current_track', vicki.player.play_from_parser)
     helper.register(self.console)
     self.console.set_queue(queue)
     th = ThreadGroup(restart=False)
     th.targets = [self.console.run_bg_queue]
     th.start_all()
     self.console.run_console()
     self.console.set_exit()
     th.stop_all()
コード例 #15
0
ファイル: station.py プロジェクト: tb0hdan/voiceplay
    def play_station(cls, station):
        """
        Play top tracks for station
        TODO: Fix in https://github.com/tb0hdan/voiceplay/issues/22
        """
        sc = StationCrawl()
        sc.put_genre(station)

        tg = ThreadGroup(restart=False)
        tg.targets = [sc.genre_loop, sc.playlist_loop]
        tg.start_all()

        already_played = []
        while True:
            for track in cls.tracks_with_prefetch(sc.playlist):
                if cls.get_exit():  # pylint:disable=no-member
                    sc.set_exit(True)
                    tg.stop_all()
                    break
                cls.play_full_track(track)
                already_played.append(track)
            if sorted(already_played) == sorted(sc.playlist) or cls.get_exit():
                break
コード例 #16
0
ファイル: vickiplayer.py プロジェクト: tb0hdan/voiceplay
class VickiPlayer(object):
    """
    Vicki player class
    """
    def __init__(self, tts=None, debug=False, player_backend='vlc'):
        self.debug = debug
        self.tts = tts
        self.queue = Queue()
        self.p_queue = Queue()
        self.prefetch_q = Queue()
        if player_backend == 'vlc':
            from .backend.vlc import VLCPlayer
            self.player = VLCPlayer()
        elif player_backend == 'mplayer':
            from .backend.mpl import MPlayer
            self.player = MPlayer()
        else:
            raise NotImplementedError('Specified player backend %r is unknown' % player_backend)
        self.shutdown_flag = False
        self.exit_task = False
        self._argparser = None
        self.player_tasks = sorted(PluginLoader().find_classes('voiceplay.player.tasks', BasePlayerTask),
                                   key=cmp_to_key(lambda x, y: cmp(x.__priority__, y.__priority__)))
        self.known_actions = self.get_actions(self.player_tasks)
        self.task_pool = []

    @property
    def argparser(self):
        """
        Managed property, returns argument parser
        """
        return self._argparser

    @argparser.setter
    def argparser(self, argobj):
        """
        Managed property, sets argument parser
        """
        self._argparser = argobj
        self.player.set_argparser(argobj)

    @staticmethod
    def get_actions(tasks):
        """
        Returns list of available actions
        """
        actions = []
        for task in tasks:
            for action in task.__group__:
                if not action in actions:
                    actions.append(action)
        return actions

    def get_exit(self):
        """
        Returns true if exit was requested
        """
        return self.exit_task

    def put(self, message):
        """
        Put message in queue for processing
        """
        self.queue.put(message)

    def parse(self, message):
        """
        Parse incoming message
        """
        start = False
        action_phrase = []
        for word in message.split(' '):
            if word.lower() in self.known_actions:
                start = True
            if start and word:
                action_phrase.append(word)
        response = ' '.join(action_phrase)
        return response

    def play_from_parser(self, message):
        """
        Handle message and call respective actions
        """
        orig_message = message
        command = Command(message)
        if command.IN_ALL_SET:
            if message == command.STOP:
                self.player.stop()
                self.exit_task = True
            elif message == command.NEXT:
                self.player.resume()
                self.player.stop()
            elif message == command.PAUSE:
                self.player.pause()
            elif message == command.RESUME:
                self.player.resume()
            elif message == command.SHUTDOWN:
                self.player.shutdown()
                self.shutdown_flag = True
                self.exit_task = True
            # pass other messages
            elif command.COMMAND in [command.LOVE, command.BAN]:
                self.p_queue.put(orig_message)
        else:
            self.exit_task = True
            if message.startswith('play'):
                self.player.stop()
            self.p_queue.put(orig_message)
        return None, False

    def cmd_loop(self):
        """
        Run command loop / periodically check for commands from queue
        """
        while not self.shutdown_flag:
            if not self.queue.empty():
                message = self.queue.get()
            else:
                time.sleep(0.01)
                continue
            self.play_from_parser(message)
            self.queue.task_done()
        logger.debug('Vickiplayer.cmd_loop exit')

    def run_player_tasks(self, message):
        """
        Run player tasks
        """
        ran = False
        for task in self.player_tasks:
            for regexp in task.__regexp__:
                if re.match(regexp, message, re.I) is not None:
                    start = time.time()
                    ran = True
                    task.prefetch_callback = self.add_to_prefetch_q
                    task.argparser = self._argparser
                    task.get_exit = self.get_exit
                    task.player = self.player
                    task.tts = self.tts
                    th = threading.Thread(target=task.process, args=(regexp, message))
                    th.daemon = True
                    th.start()
                    self.task_pool.append(th)
                    logger.debug('Task %r completed in %ss', task, '{0:.2f}'.format(int(time.time()) - start))
                    break
            if ran:
                break
        return ran

    def task_loop(self):
        """
        Check periodically for tasks from queue
        """
        while not self.shutdown_flag:
            if not self.p_queue.empty():
                parsed = self.parse(self.p_queue.get())
            else:
                time.sleep(0.01)
                continue
            logger.debug('task_loop got from queue: %r', parsed)
            if not parsed:
                continue
            self.exit_task = False
            ran = self.run_player_tasks(parsed)
            if not ran:
                msg = 'I think you said ' + parsed
                self.tts.say_put(msg)
                logger.warning(msg)
            self.p_queue.task_done()
        logger.debug('VickiPlayer.task_loop exit')

    def prefetch_loop(self):
        """
        Prefetch loop allows significant speedup in playback as it fetches tracks in background
        """
        while not self.shutdown_flag:
            if not self.prefetch_q.empty():
                trackname = self.prefetch_q.get()
            else:
                time.sleep(0.01)
                continue
            logger.debug('prefetch_loop got from queue: %r', trackname.encode('utf-8'))
            start = time.time()
            BasePlayerTask.search_full_track(trackname, download=True)
            elapsed = round(time.time() - start, 3)
            logger.debug('prefetch_loop finished downloading, took %ss', elapsed)

    def add_to_prefetch_q(self, item):
        """
        Add track to prefetch queue
        """
        self.prefetch_q.put(item)

    def start(self):
        """
        Start VickiPlayer and auxiliary loops using thread group
        """
        # non-blocking start
        self.player.start(debug=self.debug)
        self.thread_group = ThreadGroup()
        self.thread_group.targets = [self.task_loop, self.cmd_loop, self.prefetch_loop]
        self.thread_group.start_all()

    def shutdown(self):
        """
        Shutdown VickiPlayer and auxiliary loops
        """
        self.shutdown_flag = True
        self.exit_task = True
        for th in self.task_pool:
            th.join(timeout=1)
        self.player.shutdown()
        self.thread_group.stop_all()
コード例 #17
0
ファイル: voiceplay_osx.py プロジェクト: tb0hdan/voiceplay
 def __run_bg__(self):
     self.th = ThreadGroup(restart=False)
     self.th.targets = [self.__run_console__]
     self.th.start_all()
コード例 #18
0
ファイル: tts.py プロジェクト: tb0hdan/voiceplay
class TextToSpeech(object):
    """
    MAC/Linux TTS
    """
    def __init__(self):
        self.thread = None
        self.shutdown = False
        system = platform.system()
        if system == 'Darwin':
            try:
                from AppKit import NSSpeechSynthesizer
                voice = 'Vicki'
                base = 'com.apple.speech.synthesis.voice'
                self.voice = base + '.' + voice
                self.speech = NSSpeechSynthesizer.alloc().initWithVoice_(self.voice)
                # sierra?
                if not self.speech:
                    self.speech = NSSpeechSynthesizer.alloc().initWithVoice_(base + '.' + 'Victoria')
                self.say = self.__say_mac
            except ImportError:
                # osx lacks appkit support for python3 (sigh)
                self.say = self.__say_dummy
        elif system == 'Linux':
            self.say = self.__say_linux
        else:
            raise NotImplementedError('Platform not supported')
        self.queue = Queue()

    @staticmethod
    def __say_linux(message):
        """
        Read message aloud (Linux)
        """
        from festival import sayText  # pylint:disable=import-error
        sayText(message)

    def __say_mac(self, message):
        """
        Read message aloud (MAC)
        """
        self.speech.startSpeakingString_(message)
        while self.speech.isSpeaking():
            time.sleep(0.1)

    def __say_dummy(self, message):
        """
        Dummy TTS, does nothing
        """
        pass

    def poll_loop(self):
        """
        Poll speech queue
        """
        while not self.shutdown:
            if not self.queue.empty():
                message = self.queue.get()
                logger.debug('TTS: %r', message)
                self.say(message)
                self.queue.task_done()
            else:
                time.sleep(0.01)
        logger.debug('TTS poll_loop exit')

    def start(self):
        """
        Start TTS as a separate thread
        """
        logger.debug('Starting TTS engine...')
        self.threads = ThreadGroup()
        self.threads.targets = [self.poll_loop]
        self.threads.start_all()

    def stop(self):
        """
        Set shutdown flag and wait for thread to exit
        """
        self.shutdown = True
        self.threads.stop_all()

    def say_put(self, message):
        """
        Add message to speech queue
        """
        self.queue.put(message)
コード例 #19
0
ファイル: mdns.py プロジェクト: tb0hdan/voiceplay
class VoicePlayZeroConf(object):
    """
    Very basic mDNS/Zeroconf check/registration
    """
    def __init__(self):
        self.zeroconf = None
        self.known_servers = []
        self.info = None
        self.threads = None
        self.exit = False

    @staticmethod
    def get_ip_address():
        """
        Get my IP
        """
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        return s.getsockname()[0]

    def service_info(self, hostname):
        """
        Prepare service information for (my) hostname
        """
        port = int(Config.cfg_data().get('webapp_port'))
        info = ServiceInfo("_http._tcp.local.",
                           "{0!s}._http._tcp.local.".format(hostname),
                           socket.inet_aton(self.get_ip_address()), port, 0, 0,
                           {'path': '/'}, "{0!s}.local.".format(hostname))
        return info

    def on_service_state_change(self, zeroconf, service_type, name,
                                state_change):
        """
        Service state change callback
        """
        if state_change is ServiceStateChange.Added:
            info = zeroconf.get_service_info(service_type, name)
            if info and not info.server in self.known_servers:
                self.known_servers.append(info.server)

    def get_others(self):
        """
        Wait for other services to make themselves visible
        """
        zeroconf = Zeroconf()
        _ = ServiceBrowser(zeroconf,
                           "_http._tcp.local.",
                           handlers=[self.on_service_state_change])
        for _ in range(1, 10 + 1):
            time.sleep(1)
        zeroconf.close()
        return self.known_servers

    def run(self):
        """
        Actual Zeroconf runner
        """
        # has to be here, otherwise quick start/stop will not provide self.zeroconf descriptor
        self.zeroconf = Zeroconf()
        servers = self.get_others()
        hostname = __title__.lower()
        if '{0!s}.local'.format(hostname) in servers:
            hostname = '{0!s}-{1!s}'.format(__title__.lower(),
                                            str(uuid.uuid4()))
        self.info = self.service_info(hostname)
        self.zeroconf.register_service(self.info)
        while not self.exit:
            try:
                time.sleep(0.1)
            except Exception as _:
                break

    def unregister(self):
        """
        Unregister (remove) service from local network
        """
        if self.info:
            self.zeroconf.unregister_service(self.info)
        self.zeroconf.close()

    def start(self):
        """
        Start Zeroconf inside thread
        """
        self.threads = ThreadGroup()
        self.threads.targets = [self.run]
        self.threads.start_all()

    def stop(self):
        """
        Stop and unregister Zeroconf service
        """
        self.exit = True
        self.unregister()
        if self.threads:
            self.threads.stop_all()
コード例 #20
0
ファイル: mdns.py プロジェクト: tb0hdan/voiceplay
class VoicePlayZeroConf(object):
    """
    Very basic mDNS/Zeroconf check/registration
    """
    def __init__(self):
        self.zeroconf = None
        self.known_servers = []
        self.info = None
        self.threads = None
        self.exit = False

    @staticmethod
    def get_ip_address():
        """
        Get my IP
        """
        s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
        s.connect(("8.8.8.8", 80))
        return s.getsockname()[0]

    def service_info(self, hostname):
        """
        Prepare service information for (my) hostname
        """
        port = int(Config.cfg_data().get('webapp_port'))
        info = ServiceInfo("_http._tcp.local.",
                           "{0!s}._http._tcp.local.".format(hostname),
                           socket.inet_aton(self.get_ip_address()), port, 0, 0,
                           {'path': '/'}, "{0!s}.local.".format(hostname))
        return info

    def on_service_state_change(self, zeroconf, service_type, name, state_change):
        """
        Service state change callback
        """
        if state_change is ServiceStateChange.Added:
            info = zeroconf.get_service_info(service_type, name)
            if info and not info.server in self.known_servers:
                self.known_servers.append(info.server)

    def get_others(self):
        """
        Wait for other services to make themselves visible
        """
        zeroconf = Zeroconf()
        _ = ServiceBrowser(zeroconf, "_http._tcp.local.", handlers=[self.on_service_state_change])
        for _ in range(1, 10 + 1):
            time.sleep(1)
        zeroconf.close()
        return self.known_servers

    def run(self):
        """
        Actual Zeroconf runner
        """
        # has to be here, otherwise quick start/stop will not provide self.zeroconf descriptor
        self.zeroconf = Zeroconf()
        servers = self.get_others()
        hostname = __title__.lower()
        if '{0!s}.local'.format(hostname) in servers:
            hostname = '{0!s}-{1!s}'.format(__title__.lower(), str(uuid.uuid4()))
        self.info = self.service_info(hostname)
        self.zeroconf.register_service(self.info)
        while not self.exit:
            try:
                time.sleep(0.1)
            except Exception as _:
                break

    def unregister(self):
        """
        Unregister (remove) service from local network
        """
        if self.info:
            self.zeroconf.unregister_service(self.info)
        self.zeroconf.close()

    def start(self):
        """
        Start Zeroconf inside thread
        """
        self.threads = ThreadGroup()
        self.threads.targets = [self.run]
        self.threads.start_all()

    def stop(self):
        """
        Stop and unregister Zeroconf service
        """
        self.exit = True
        self.unregister()
        if self.threads:
            self.threads.stop_all()
コード例 #21
0
ファイル: voiceplay_osx.py プロジェクト: tb0hdan/voiceplay
class VoicePlayApp(rumps.App):
    def __init__(self):
        super(VoicePlayApp, self).__init__(vp_title, quit_button=None)
        self.menu = ['Pause/Resume', 'Quit']

    @rumps.clicked('Pause/Resume')
    def pause_resume(self, _):
        try:
            self.console.parse_command('pause')
        except Exception as exc:
            logger.error(repr(exc))

    @rumps.clicked('Quit')
    def menu_quit(self, _):
        try:
            self.console.set_exit()
            self.vicki.player.shutdown()
            self.th.stop_all()
        except Exception as exc:
            logger.error(repr(exc))
        rumps.quit_application()

    def player_console(self, vicki, queue=None):
        """
        Start VickiPlayer console
        """
        helper = Help()
        #self.console = Console()
        self.console.add_handler(Command.PLAY, vicki.player.play_from_parser, Command().CONTROLS)
        self.console.add_handler('what', vicki.player.play_from_parser)
        self.console.add_handler('current_track', vicki.player.play_from_parser)
        helper.register(self.console)
        self.console.set_queue(queue)
        th = ThreadGroup(restart=False)
        th.targets = [self.console.run_bg_queue]
        th.start_all()
        self.console.run_console()
        self.console.set_exit()
        th.stop_all()

    def __run_console__(self):
        self.parser.player_console(self.vicki, queue=self.queue)

    def __run_bg__(self):
        self.th = ThreadGroup(restart=False)
        self.th.targets = [self.__run_console__]
        self.th.start_all()

    def run_app(self):
        signal_handler = SignalHandler()
        signal_handler.register()
        message = check_update(suppress_uptodate=True)
        if message:
            logger.error(message)
        parser = MyArgumentParser(signal_handler=signal_handler)
        parser.configure()
        # first parse is just for debug
        result = parser.parser.parse_args(sys.argv[1:])
        debug = result.debug
        #
        rumps.debug_mode(debug)
        #
        result = parser.parser.parse_args(['-c'])
        vicki = Vicki(debug=debug, player_backend=result.player_backend)
        vicki.player.player.set_argparser(result)
        #
        self.console = Console()
        #
        self.queue = multiprocessing.Queue()
        vicki.player.start()
        self.vicki = vicki
        self.parser = parser
        self.__run_bg__()
        self.run()
コード例 #22
0
ファイル: voiceplay_osx.py プロジェクト: tb0hdan/voiceplay
class VoicePlayApp(rumps.App):
    def __init__(self):
        super(VoicePlayApp, self).__init__(vp_title, quit_button=None)
        self.menu = ['Pause/Resume', 'Quit']

    @rumps.clicked('Pause/Resume')
    def pause_resume(self, _):
        try:
            self.console.parse_command('pause')
        except Exception as exc:
            logger.error(repr(exc))

    @rumps.clicked('Quit')
    def menu_quit(self, _):
        try:
            self.console.set_exit()
            self.vicki.player.shutdown()
            self.th.stop_all()
        except Exception as exc:
            logger.error(repr(exc))
        rumps.quit_application()

    def player_console(self, vicki, queue=None):
        """
        Start VickiPlayer console
        """
        helper = Help()
        #self.console = Console()
        self.console.add_handler(Command.PLAY, vicki.player.play_from_parser,
                                 Command().CONTROLS)
        self.console.add_handler('what', vicki.player.play_from_parser)
        self.console.add_handler('current_track',
                                 vicki.player.play_from_parser)
        helper.register(self.console)
        self.console.set_queue(queue)
        th = ThreadGroup(restart=False)
        th.targets = [self.console.run_bg_queue]
        th.start_all()
        self.console.run_console()
        self.console.set_exit()
        th.stop_all()

    def __run_console__(self):
        self.parser.player_console(self.vicki, queue=self.queue)

    def __run_bg__(self):
        self.th = ThreadGroup(restart=False)
        self.th.targets = [self.__run_console__]
        self.th.start_all()

    def run_app(self):
        signal_handler = SignalHandler()
        signal_handler.register()
        message = check_update(suppress_uptodate=True)
        if message:
            logger.error(message)
        parser = MyArgumentParser(signal_handler=signal_handler)
        parser.configure()
        # first parse is just for debug
        result = parser.parser.parse_args(sys.argv[1:])
        debug = result.debug
        #
        rumps.debug_mode(debug)
        #
        result = parser.parser.parse_args(['-c'])
        vicki = Vicki(debug=debug, player_backend=result.player_backend)
        vicki.player.player.set_argparser(result)
        #
        self.console = Console()
        #
        self.queue = multiprocessing.Queue()
        vicki.player.start()
        self.vicki = vicki
        self.parser = parser
        self.__run_bg__()
        self.run()
コード例 #23
0
ファイル: voiceplay_osx.py プロジェクト: tb0hdan/voiceplay
 def __run_bg__(self):
     self.th = ThreadGroup(restart=False)
     self.th.targets = [self.__run_console__]
     self.th.start_all()
コード例 #24
0
class VickiPlayer(object):
    """
    Vicki player class
    """
    def __init__(self, tts=None, debug=False, player_backend='vlc'):
        self.debug = debug
        self.tts = tts
        self.queue = Queue()
        self.p_queue = Queue()
        self.prefetch_q = Queue()
        if player_backend == 'vlc':
            from .backend.vlc import VLCPlayer
            self.player = VLCPlayer()
        elif player_backend == 'mplayer':
            from .backend.mpl import MPlayer
            self.player = MPlayer()
        else:
            raise NotImplementedError(
                'Specified player backend %r is unknown' % player_backend)
        self.shutdown_flag = False
        self.exit_task = False
        self._argparser = None
        self.player_tasks = sorted(
            PluginLoader().find_classes('voiceplay.player.tasks',
                                        BasePlayerTask),
            key=cmp_to_key(lambda x, y: cmp(x.__priority__, y.__priority__)))
        self.known_actions = self.get_actions(self.player_tasks)
        self.task_pool = []

    @property
    def argparser(self):
        """
        Managed property, returns argument parser
        """
        return self._argparser

    @argparser.setter
    def argparser(self, argobj):
        """
        Managed property, sets argument parser
        """
        self._argparser = argobj
        self.player.set_argparser(argobj)

    @staticmethod
    def get_actions(tasks):
        """
        Returns list of available actions
        """
        actions = []
        for task in tasks:
            for action in task.__group__:
                if not action in actions:
                    actions.append(action)
        return actions

    def get_exit(self):
        """
        Returns true if exit was requested
        """
        return self.exit_task

    def put(self, message):
        """
        Put message in queue for processing
        """
        self.queue.put(message)

    def parse(self, message):
        """
        Parse incoming message
        """
        start = False
        action_phrase = []
        for word in message.split(' '):
            if word.lower() in self.known_actions:
                start = True
            if start and word:
                action_phrase.append(word)
        response = ' '.join(action_phrase)
        return response

    def play_from_parser(self, message):
        """
        Handle message and call respective actions
        """
        orig_message = message
        command = Command(message)
        if command.IN_ALL_SET:
            if message == command.STOP:
                self.player.stop()
                self.exit_task = True
            elif message == command.NEXT:
                self.player.resume()
                self.player.stop()
            elif message == command.PAUSE:
                self.player.pause()
            elif message == command.RESUME:
                self.player.resume()
            elif message == command.SHUTDOWN:
                self.player.shutdown()
                self.shutdown_flag = True
                self.exit_task = True
            # pass other messages
            elif command.COMMAND in [command.LOVE, command.BAN]:
                self.p_queue.put(orig_message)
        else:
            self.exit_task = True
            if message.startswith('play'):
                self.player.stop()
            self.p_queue.put(orig_message)
        return None, False

    def cmd_loop(self):
        """
        Run command loop / periodically check for commands from queue
        """
        while not self.shutdown_flag:
            if not self.queue.empty():
                message = self.queue.get()
            else:
                time.sleep(0.01)
                continue
            self.play_from_parser(message)
            self.queue.task_done()
        logger.debug('Vickiplayer.cmd_loop exit')

    def run_player_tasks(self, message):
        """
        Run player tasks
        """
        ran = False
        for task in self.player_tasks:
            for regexp in task.__regexp__:
                if re.match(regexp, message, re.I) is not None:
                    start = time.time()
                    ran = True
                    task.prefetch_callback = self.add_to_prefetch_q
                    task.argparser = self._argparser
                    task.get_exit = self.get_exit
                    task.player = self.player
                    task.tts = self.tts
                    th = threading.Thread(target=task.process,
                                          args=(regexp, message))
                    th.daemon = True
                    th.start()
                    self.task_pool.append(th)
                    logger.debug('Task %r completed in %ss', task,
                                 '{0:.2f}'.format(int(time.time()) - start))
                    break
            if ran:
                break
        return ran

    def task_loop(self):
        """
        Check periodically for tasks from queue
        """
        while not self.shutdown_flag:
            if not self.p_queue.empty():
                parsed = self.parse(self.p_queue.get())
            else:
                time.sleep(0.01)
                continue
            logger.debug('task_loop got from queue: %r', parsed)
            if not parsed:
                continue
            self.exit_task = False
            ran = self.run_player_tasks(parsed)
            if not ran:
                msg = 'I think you said ' + parsed
                self.tts.say_put(msg)
                logger.warning(msg)
            self.p_queue.task_done()
        logger.debug('VickiPlayer.task_loop exit')

    def prefetch_loop(self):
        """
        Prefetch loop allows significant speedup in playback as it fetches tracks in background
        """
        while not self.shutdown_flag:
            if not self.prefetch_q.empty():
                trackname = self.prefetch_q.get()
            else:
                time.sleep(0.01)
                continue
            logger.debug('prefetch_loop got from queue: %r',
                         trackname.encode('utf-8'))
            start = time.time()
            BasePlayerTask.search_full_track(trackname, download=True)
            elapsed = round(time.time() - start, 3)
            logger.debug('prefetch_loop finished downloading, took %ss',
                         elapsed)

    def add_to_prefetch_q(self, item):
        """
        Add track to prefetch queue
        """
        self.prefetch_q.put(item)

    def start(self):
        """
        Start VickiPlayer and auxiliary loops using thread group
        """
        # non-blocking start
        self.player.start(debug=self.debug)
        self.thread_group = ThreadGroup()
        self.thread_group.targets = [
            self.task_loop, self.cmd_loop, self.prefetch_loop
        ]
        self.thread_group.start_all()

    def shutdown(self):
        """
        Shutdown VickiPlayer and auxiliary loops
        """
        self.shutdown_flag = True
        self.exit_task = True
        for th in self.task_pool:
            th.join(timeout=1)
        self.player.shutdown()
        self.thread_group.stop_all()