aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--voctocore/lib/commands.py207
-rw-r--r--voctocore/lib/controlserver.py33
-rw-r--r--voctocore/lib/notifications.py14
-rw-r--r--voctocore/lib/response.py15
-rw-r--r--voctocore/lib/videomix.py3
-rwxr-xr-xvoctocore/voctocore.py3
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()