Somewhat fancy voice command recognition software
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

mpris.py 4.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. # This is part of Kaylee
  2. # -- this code is licensed GPLv3
  3. # Copyright 2015-2017 Clayton G. Hobbs
  4. # Portions Copyright 2013 Jezra
  5. """MPRIS media player control plugin
  6. This plugin allows control of a media player supporting the Media
  7. Player Remote Interfacing Specification (MPRIS) D-Bus interface. To
  8. use this plugin, you must first install `pydbus
  9. <https://github.com/LEW21/pydbus>`__ for Python 3. This plugin takes
  10. no configuration, so load it as follows::
  11. ".mpris": {}
  12. Note: if your microphone can hear the audio produced by your speakers,
  13. it is recommended to load PulseAudio's module-echo-cancel when using
  14. this plugin. Recognition while music is playing may still be mediocre,
  15. but without module-echo-cancel it has almost no chance of working.
  16. """
  17. from gi.repository import GLib
  18. from pydbus import SessionBus
  19. from . import PluginBase, Handler, HandlerFailure
  20. class Plugin(PluginBase):
  21. """Main class for the MPRIS plugin"""
  22. def __init__(self, config, name):
  23. """Initialize the MPRIS plugin"""
  24. super().__init__(config, name)
  25. # Get the D-Bus proxy object
  26. self._bus = SessionBus()
  27. self._dbus_proxy = self._bus.get('.DBus')
  28. self.commands = {
  29. 'next song': MPRISNextHandler,
  30. 'next video': MPRISNextHandler,
  31. 'pause music': MPRISPauseHandler,
  32. 'pause video': MPRISPauseHandler,
  33. 'play music': MPRISPlayHandler,
  34. 'play video': MPRISPlayHandler,
  35. 'previous song': MPRISPreviousHandler,
  36. 'previous video': MPRISPreviousHandler,
  37. 'whats playing': MPRISTrackInfoHandler
  38. }
  39. self.corpus_strings.update(self.commands)
  40. def _mpris_proxy(self):
  41. """Get a proxy for the first MPRIS media player, or None"""
  42. try:
  43. # Get the bus name of the first MPRIS media player
  44. bus_name = [name for name in self._dbus_proxy.ListNames()
  45. if name.startswith('org.mpris.MediaPlayer2')][0]
  46. # Get the proxy
  47. return self._bus.get(bus_name, '/org/mpris/MediaPlayer2')
  48. except IndexError:
  49. return None
  50. def get_handler(self, text):
  51. """Return a handler if a recognized command is heard"""
  52. if text not in self.corpus_strings:
  53. return None
  54. p = self._mpris_proxy()
  55. if p is None:
  56. return None
  57. # Make a handler for the command we heard
  58. return self.commands[text](1, p)
  59. class MPRISHandler(Handler):
  60. """Base class for MPRIS plugin handlers"""
  61. def __init__(self, confidence, proxy):
  62. """Store the MPRIS proxy object"""
  63. super().__init__(confidence)
  64. self.proxy = proxy
  65. class MPRISNextHandler(MPRISHandler):
  66. """Handler for the MPRIS Next method"""
  67. def __call__(self, tts):
  68. """Call the MPRIS Next method"""
  69. try:
  70. self.proxy.Next()
  71. except GLib.Error:
  72. raise HandlerFailure()
  73. class MPRISPauseHandler(MPRISHandler):
  74. """Handler for the MPRIS Pause method"""
  75. def __call__(self, tts):
  76. """Call the MPRIS Pause method"""
  77. try:
  78. self.proxy.Pause()
  79. except GLib.Error:
  80. raise HandlerFailure()
  81. class MPRISPlayHandler(MPRISHandler):
  82. """Handler for the MPRIS Play method"""
  83. def __call__(self, tts):
  84. """Call the MPRIS Play method"""
  85. try:
  86. self.proxy.Play()
  87. except GLib.Error:
  88. raise HandlerFailure()
  89. class MPRISPreviousHandler(MPRISHandler):
  90. """Handler for the MPRIS Previous method"""
  91. def __call__(self, tts):
  92. """Call the MPRIS Previous method"""
  93. try:
  94. self.proxy.Previous()
  95. except GLib.Error:
  96. raise HandlerFailure()
  97. class MPRISTrackInfoHandler(MPRISHandler):
  98. """Handler for getting info about the currently playing track from MPRIS"""
  99. def __call__(self, tts):
  100. """Get and speak info about the currently playing track"""
  101. # Get metadata about the player
  102. try:
  103. metadata = self.proxy.Metadata
  104. except GLib.Error:
  105. raise HandlerFailure()
  106. # Get the title
  107. try:
  108. title = metadata['xesam:title']
  109. except KeyError:
  110. raise HandlerFailure()
  111. # Get the artist list (optional)
  112. try:
  113. artist_list = metadata['xesam:artist']
  114. except KeyError:
  115. artist_text = ''
  116. else:
  117. # Get the artists in a speakable format
  118. artist_text = ' by ' + ', '.join(
  119. artist_list[:-2] + [', and '.join(artist_list[-2:])])
  120. # Speak the information
  121. tts(f"{title}{artist_text}")