summaryrefslogtreecommitdiff
path: root/voctocore/lib/commands.py
blob: 11cacdd466b766f10d3dd3d9d57d4f5736848862 (plain)
  1. import logging
  2. import json
  3. import inspect
  4. from lib.config import Config
  5. from lib.videomix import CompositeModes
  6. from lib.response import NotifyResponse, OkResponse
  7. def decodeName(items, name_or_id):
  8. try:
  9. name_or_id = int(name_or_id)
  10. if name_or_id < 0 or name_or_id >= len(items):
  11. raise IndexError("unknown index %d" % name_or_id)
  12. return name_or_id
  13. except ValueError as e:
  14. try:
  15. return items.index(name_or_id)
  16. except ValueError as e:
  17. raise IndexError("unknown name %s" % name_or_id)
  18. def decodeEnumName(enum, name_or_id):
  19. try:
  20. name_or_id = int(name_or_id)
  21. if name_or_id < 0 or name_or_id >= len(enum):
  22. raise IndexError("unknown index %d" % name_or_id)
  23. return name_or_id
  24. except ValueError as e:
  25. try:
  26. return enum[name_or_id]
  27. except KeyError as e:
  28. raise IndexError("unknown name %s" % name_or_id)
  29. def encodeName(items, id):
  30. try:
  31. return items[id]
  32. except IndexError as e:
  33. raise IndexError("unknown index %d" % id)
  34. def encodeEnumName(enum, id):
  35. try:
  36. return enum(id).name
  37. except ValueError as e:
  38. raise IndexError("unknown index %d" % id)
  39. class ControlServerCommands(object):
  40. def __init__(self, pipeline):
  41. self.log = logging.getLogger('ControlServerCommands')
  42. self.pipeline = pipeline
  43. self.sources = Config.getlist('mix', 'sources')
  44. self.blankerSources = Config.getlist('stream-blanker', 'sources')
  45. # Commands are defined below. Errors are sent to the clients by throwing
  46. # exceptions, they will be turned into messages outside.
  47. def message(self, *args):
  48. """sends a message through the control-server, which can be received by
  49. user-defined scripts. does not change the state of the voctocore."""
  50. return NotifyResponse('message', *args)
  51. def help(self):
  52. helplines = []
  53. helplines.append("Commands:")
  54. for name, func in ControlServerCommands.__dict__.items():
  55. if name[0] == '_':
  56. continue
  57. if not func.__code__:
  58. continue
  59. params = inspect.signature(func).parameters
  60. params = [str(info) for name, info in params.items()]
  61. params = ', '.join(params[1:])
  62. command_sig = '\t' + name
  63. if params:
  64. command_sig += ': '+params
  65. if func.__doc__:
  66. command_sig += '\n'+'\n'.join(
  67. ['\t\t'+line.strip() for line in func.__doc__.splitlines()])+'\n'
  68. helplines.append(command_sig)
  69. helplines.append('\t'+'quit')
  70. helplines.append("\n")
  71. helplines.append("Source-Names:")
  72. for source in self.sources:
  73. helplines.append("\t"+source)
  74. helplines.append("\n")
  75. helplines.append("Stream-Blanker Sources-Names:")
  76. for source in self.blankerSources:
  77. helplines.append("\t"+source)
  78. helplines.append("\n")
  79. helplines.append("Composition-Modes:")
  80. for mode in CompositeModes:
  81. helplines.append("\t"+mode.name)
  82. return OkResponse("\n".join(helplines))
  83. def _get_video_status(self):
  84. a = encodeName( self.sources, self.pipeline.vmix.getVideoSourceA() )
  85. b = encodeName( self.sources, self.pipeline.vmix.getVideoSourceB() )
  86. return [a, b]
  87. def get_video(self):
  88. status = self._get_video_status()
  89. return OkResponse('video_status', *status)
  90. def set_video_a(self, src_name_or_id):
  91. src_id = decodeName(self.sources, src_name_or_id)
  92. self.pipeline.vmix.setVideoSourceA(src_id)
  93. status = self._get_video_status()
  94. return NotifyResponse('video_status', *status)
  95. def set_video_b(self, src_name_or_id):
  96. src_id = decodeName(self.sources, src_name_or_id)
  97. self.pipeline.vmix.setVideoSourceB(src_id)
  98. status = self._get_video_status()
  99. return NotifyResponse('video_status', *status)
  100. def _get_audio_status(self):
  101. src_id = self.pipeline.amix.getAudioSource()
  102. return encodeName(self.sources, src_id)
  103. def get_audio(self):
  104. status = self._get_audio_status()
  105. return OkResponse('audio_status', status)
  106. def set_audio(self, src_name_or_id):
  107. src_id = decodeName(self.sources, src_name_or_id)
  108. self.pipeline.amix.setAudioSource(src_id)
  109. status = self._get_audio_status()
  110. return NotifyResponse('audio_status', status)
  111. def _get_composite_status(self):
  112. mode = self.pipeline.vmix.getCompositeMode()
  113. return encodeEnumName(CompositeModes, mode)
  114. def get_composite_mode(self):
  115. status = self._get_composite_status()
  116. return OkResponse('composite_mode', status)
  117. def set_composite_mode(self, mode_name_or_id):
  118. mode = decodeEnumName(CompositeModes, mode_name_or_id)
  119. self.pipeline.vmix.setCompositeMode(mode)
  120. composite_status = self._get_composite_status()
  121. video_status = self._get_video_status()
  122. return [
  123. NotifyResponse('composite_mode', composite_status),
  124. NotifyResponse('video_status', *video_status)
  125. ]
  126. def set_videos_and_composite(self, src_a_name_or_id, src_b_name_or_id, mode_name_or_id):
  127. if src_a_name_or_id != '*':
  128. src_a_id = decodeName(self.sources, src_a_name_or_id)
  129. self.pipeline.vmix.setVideoSourceA(src_a_id)
  130. if src_b_name_or_id != '*':
  131. src_b_id = decodeName(self.sources, src_b_name_or_id)
  132. self.pipeline.vmix.setVideoSourceB(src_b_id)
  133. if mode_name_or_id != '*':
  134. mode = decodeEnumName(CompositeModes, mode_name_or_id)
  135. self.pipeline.vmix.setCompositeMode(mode)
  136. composite_status = self._get_composite_status()
  137. video_status = self._get_video_status()
  138. return [
  139. NotifyResponse('composite_mode', composite_status),
  140. NotifyResponse('video_status', *video_status)
  141. ]
  142. def _get_stream_status(self):
  143. blankSource = self.pipeline.streamblanker.blankSource
  144. if blankSource is None:
  145. return ('live',)
  146. return 'blank', encodeName(self.blankerSources, blankSource)
  147. def get_stream_status(self):
  148. status = self._get_stream_status()
  149. return OkResponse('stream_status', *status)
  150. def set_stream_blank(self, source_name_or_id):
  151. src_id = decodeName(self.blankerSources, source_name_or_id)
  152. self.pipeline.streamblanker.setBlankSource(src_id)
  153. status = self._get_stream_status()
  154. return NotifyResponse('stream_status', *status)
  155. def set_stream_live(self):
  156. self.pipeline.streamblanker.setBlankSource(None)
  157. status = self._get_stream_status()
  158. return NotifyResponse('stream_status', *status)
  159. def get_config(self):
  160. confdict = {header: dict(section) for header, section in dict(Config).items()}
  161. return OkResponse('server_config', json.dumps(confdict))