diff options
-rw-r--r-- | voctocore/lib/commands.py | 207 | ||||
-rw-r--r-- | voctocore/lib/controlserver.py | 33 | ||||
-rw-r--r-- | voctocore/lib/notifications.py | 14 | ||||
-rw-r--r-- | voctocore/lib/response.py | 15 | ||||
-rw-r--r-- | voctocore/lib/videomix.py | 3 | ||||
-rwxr-xr-x | voctocore/voctocore.py | 3 |
6 files changed, 149 insertions, 126 deletions
diff --git a/voctocore/lib/commands.py b/voctocore/lib/commands.py index c972c69..dd4ef0d 100644 --- a/voctocore/lib/commands.py +++ b/voctocore/lib/commands.py @@ -2,127 +2,146 @@ import logging import json - from lib.config import Config from lib.videomix import CompositeModes +from lib.response import NotifyResponse, OkResponse -class ControlServerCommands(): - def __init__(self, pipeline): - self.log = logging.getLogger('ControlServerCommands') +def decodeName(items, name_or_id): + try: + name_or_id = int(name_or_id) + if name_or_id < 0 or name_or_id >= len(items): + raise IndexError("unknown index %d" % name_or_id) - self.pipeline = pipeline - self.sources = Config.getlist('mix', 'sources') - self.blankersources = Config.getlist('stream-blanker', 'sources') + return name_or_id - def decodeSourceName(self, src_name_or_id): - if isinstance(src_name_or_id, str): - try: - return self.sources.index(src_name_or_id) - except Exception as e: - raise IndexError("source %s unknown" % src_name_or_id) + except ValueError as e: + try: + return items.index(name_or_id) + + except ValueError as e: + raise IndexError("unknown name %s" % name_or_id) + +def decodeEnumName(enum, name_or_id): + try: + name_or_id = int(name_or_id) + if name_or_id < 0 or name_or_id >= len(enum): + raise IndexError("unknown index %d" % name_or_id) - if src_name_or_id < 0 or src_name_or_id >= len(self.sources): - raise IndexError("source %s unknown" % src_name_or_id) + return name_or_id - def encodeSourceName(self, src_id): + except ValueError as e: try: - return self.sources[src_id] - except Exception as e: - raise IndexError("source %s unknown" % src_id) + return enum[name_or_id] - def decodeBlankerSourceName(self, src_name_or_id): - if isinstance(src_name_or_id, str): - try: - return self.blankersources.index(src_name_or_id) - except Exception as e: - raise IndexError("source %s unknown" % src_name_or_id) + except KeyError as e: + raise IndexError("unknown name %s" % name_or_id) - if src_name_or_id < 0 or src_name_or_id >= len(self.blankersources): - raise IndexError("source %s unknown" % src_name_or_id) +def encodeName(items, id): + try: + return items[id] + except IndexError as e: + raise IndexError("unknown index %d" % id) - def encodeBlankerSourceName(self, src_id): - try: - return self.blankersources[src_id] - except Exception as e: - raise IndexError("source %s unknown" % src_id) +def encodeEnumName(enum, id): + try: + return enum(id).name + except ValueError as e: + raise IndexError("unknown index %d" % id) + +class ControlServerCommands(object): + def __init__(self, pipeline): + self.log = logging.getLogger('ControlServerCommands') + + self.pipeline = pipeline + + self.sources = Config.getlist('mix', 'sources') + self.blankerSources = Config.getlist('stream-blanker', 'sources') # Commands are defined below. Errors are sent to the clients by throwing # exceptions, they will be turned into messages outside. - def fetch(self, command): - if command not in ['set', 'get', 'message', 'signal']: - raise Exception("unknown command") - return getattr(self, command) - def message(self, *args): - return " ".join(args), True + return NotifyResponse('message', args) - def signal(self, *args): - return "", True - def get(self, subcommand, *args, signal=False): - return getattr(self, "_get_"+subcommand)(*args), signal + def _get_video_status(self): + a = encodeName( self.sources, self.pipeline.vmix.getVideoSourceA() ) + b = encodeName( self.sources, self.pipeline.vmix.getVideoSourceB() ) + return [a, b] - def set(self, subcommand, *args): - getattr(self, "_set_"+subcommand)(*args) - return self.get(subcommand, *args, signal=True) + def get_video(self): + status = self._get_video_status() + return OkResponse('videoStatus', *status) - def _get_video(self, target, _=None): - if target not in ['a', 'b']: - raise Exception("invalid video source name: 'a' or 'b' expected.") - cmd = "getVideoSource" + target.upper() - src_id = getattr(self.pipeline.vmix, cmd)() - return self.encodeSourceName(src_id) + def set_video_a(self, src_name_or_id): + src_id = decodeName(self.sources, src_name_or_id) + self.pipeline.vmix.setVideoSourceA(src_id) - def _set_video(self, target, src_name_or_id): - if target not in ['a', 'b']: - raise Exception("invalid video source name: 'a' or 'b' expected.") - src_id = self.decodeSourceName(src_name_or_id) - getattr(self.pipeline.vmix, 'setVideoSource' + target.upper())(src_id) + status = self._get_video_status() + return NotifyResponse('videoStatus', *status) - def _set_audio(self, src_name_or_id): - src_id = self.decodeSourceName(src_name_or_id) - self.pipeline.amix.setAudioSource(src_id) + def set_video_b(self, src_name_or_id): + src_id = decodeName(self.sources, src_name_or_id) + self.pipeline.vmix.setVideoSourceB(src_id) + + status = self._get_video_status() + return NotifyResponse('videoStatus', *status) - def _get_audio(self, _=None): + + def _get_audio_status(self): src_id = self.pipeline.amix.getAudioSource() - return self.encodeSourceName(src_id) + return encodeName(self.sources, src_id) - def _set_composite(self, composite_mode): - try: - mode = CompositeModes[composite_mode] - except KeyError as e: - raise KeyError("composite-mode %s unknown" % composite_mode) + def get_audio(self): + status = self._get_audio_status() + return OkResponse('audioStatus', status) + + def set_audio(self, src_name_or_id): + src_id = decodeName(self.sources, src_name_or_id) + self.pipeline.amix.setAudioSource(src_id) + status = self._get_audio_status() + return NotifyResponse('audioStatus', status) + + + def _get_composite_status(self): + mode = self.pipeline.vmix.getCompositeMode() + return encodeEnumName(CompositeModes, mode) + + def get_composite(self): + status = self._get_composite_status() + return OkResponse('compositeMode', status) + + def set_composite(self, mode_name_or_id): + mode = decodeEnumName(CompositeModes, mode_name_or_id) self.pipeline.vmix.setCompositeMode(mode) - def _get_composite(self, _=None): - try: - mode = self.pipeline.vmix.getCompositeMode() - return mode.name - except Exception as e: - raise KeyError("composite-mode %s unknown" % mode) + status = self._get_composite_status() + return NotifyResponse('compositeMode', status) - def _set_status(self, *args): - try: - if args[0] == "live": - self.pipeline.streamblanker.setBlankSource(None) - elif args [0] == "blank": - src_id = self.decodeBlankerSourceName(args[1]) - self.pipeline.streamblanker.setBlankSource(src_id) - else: - raise IndexError() - except IndexError as e: - raise Exception("invocation: set_status (live | blank <mode>)") - - def _get_status(self, *args): - if self.pipeline.streamblanker.blankSource is None: - return "live" - - name = self.encodeBlankerSourceName(self.pipeline.streamblanker.blankSource) - return "blank " + name - - def _get_config(self): - confdict = {k: dict(v) for k, v in dict(Config).items()} - return json.dumps(confdict) + def _get_stream_status(self): + blankSource = self.pipeline.streamblanker.blankSource + return encodeName(self.blankerSources, blankSource) + + def get_stream_status(self): + status = self._get_stream_status() + return OkResponse('streamStatus', status) + + def set_stream_blank(self, source_name_or_id): + src_id = decodeName(self.blankerSources, source_name_or_id) + self.pipeline.streamblanker.setBlankSource(src_id) + + status = self._get_stream_status() + return NotifyResponse('streamStatus', status) + + def set_stream_live(self): + self.pipeline.streamblanker.setBlankSource(None) + + status = self._get_stream_status() + return NotifyResponse('streamStatus', status) + + + def get_config(self): + confdict = {header: dict(section) for header, section in dict(Config).items()} + return json.dumps(confdict) diff --git a/voctocore/lib/controlserver.py b/voctocore/lib/controlserver.py index 4e95e46..e0632fc 100644 --- a/voctocore/lib/controlserver.py +++ b/voctocore/lib/controlserver.py @@ -5,6 +5,7 @@ from gi.repository import GObject from lib.commands import ControlServerCommands from lib.tcpmulticonnection import TCPMultiConnection +from lib.response import NotifyResponse, OkResponse class ControlServer(TCPMultiConnection): def __init__(self, pipeline): @@ -73,21 +74,29 @@ class ControlServer(TCPMultiConnection): args = words[1:] try: - f = self.commands.fetch(command) - message, send_signals = f(*args) - response = "ok %s\n" % message + command_function = self.commands.__class__.__dict__[command] - except Exception as e: - message = str(e) or "<no message>" - response = "error %s\n" % message + except KeyError as e: + response = "error unknown command %s\n" % command else: - if send_signals: - signal = "signal %s\n" % line - for conn, queue in self.currentConnections.items(): - if conn == requestor: - continue - queue.put(signal) + try: + responseObject = command_function(self.commands, *args) + + except Exception as e: + message = str(e) or "<no message>" + response = "error %s\n" % message + + else: + if isinstance(responseObject, NotifyResponse): + signal = "%s\n" % str(responseObject) + for conn, queue in self.currentConnections.items(): + if conn == requestor: + continue + + queue.put(signal) + + response = "%s\n" % str(responseObject) finally: self.currentConnections[requestor].put(response) diff --git a/voctocore/lib/notifications.py b/voctocore/lib/notifications.py deleted file mode 100644 index 1e101e1..0000000 --- a/voctocore/lib/notifications.py +++ /dev/null @@ -1,14 +0,0 @@ -#!/usr/bin/python3 - -# If you know a better way to do this, go for it... - -import logging - -log = logging.getLogger("Notifications") -controlserver = None - -def notify_all(msg): - try: - controlserver.notify_all(msg) - except Exception as e: - log.warn(e) diff --git a/voctocore/lib/response.py b/voctocore/lib/response.py new file mode 100644 index 0000000..866d5f2 --- /dev/null +++ b/voctocore/lib/response.py @@ -0,0 +1,15 @@ +#!/usr/bin/python3 + +class Response(object): + def __init__(self, *args): + self.args = args + + def __str__(self): + return " ".join(map(str, self.args)) + + +class OkResponse(Response): + pass + +class NotifyResponse(Response): + pass diff --git a/voctocore/lib/videomix.py b/voctocore/lib/videomix.py index 053219a..e0beefb 100644 --- a/voctocore/lib/videomix.py +++ b/voctocore/lib/videomix.py @@ -4,7 +4,6 @@ from gi.repository import Gst from enum import Enum from lib.config import Config -from lib.notifications import notify_all class CompositeModes(Enum): fullscreen = 0 @@ -313,7 +312,6 @@ class VideoMix(object): # swap if required if self.sourceB == source: self.sourceB = self.sourceA - notify_all("signal set video b %s\n" % self.sourceB) self.sourceA = source self.recalculateMixerState() @@ -325,7 +323,6 @@ class VideoMix(object): # swap if required if self.sourceA == source: self.sourceA = self.sourceB - notify_all("signal set video b %s\n" % self.sourceA) self.sourceB = source self.recalculateMixerState() diff --git a/voctocore/voctocore.py b/voctocore/voctocore.py index 0e65bc6..609ef52 100755 --- a/voctocore/voctocore.py +++ b/voctocore/voctocore.py @@ -24,7 +24,6 @@ Gst.init([]) from lib.args import Args from lib.pipeline import Pipeline from lib.controlserver import ControlServer -from lib import notifications # main class class Voctocore(object): @@ -82,8 +81,6 @@ def main(): logging.debug('initializing Voctocore') voctocore = Voctocore() - notifications.controlserver = voctocore.controlserver - logging.debug('running Voctocore') voctocore.run() |