cuemsutils 0.0.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,309 @@
1
+ from threading import Thread
2
+ from time import sleep
3
+
4
+ from collections.abc import Mapping
5
+ # from os import path
6
+ # from pyossia import ossia
7
+ from Cue import Cue
8
+ # from ..players.DmxPlayer import DmxPlayer
9
+ # from ..OssiaServer import OssiaServer, OSCConfData, PlayerOSCConfData
10
+ from cuemsutils.log import logged, Logger
11
+
12
+ #### TODO: asegurar asignacion de escenas a cue, no copia!!
13
+
14
+ class DmxCue(Cue):
15
+ # OSC_DMXPLAYER_CONF = { '/quit' : [ossia.ValueType.Impulse, None],
16
+ # '/load' : [ossia.ValueType.String, None],
17
+ # '/wait' : [ossia.ValueType.Float, None],
18
+ # '/play' : [ossia.ValueType.Impulse, None],
19
+ # '/stop' : [ossia.ValueType.Impulse, None],
20
+ # '/stoponlost' : [ossia.ValueType.Bool, None],
21
+ # # TODO '/mtcfollow' : [ossia.ValueType.Bool, None],
22
+ # '/check' : [ossia.ValueType.Impulse, None]
23
+ # }
24
+
25
+ def __init__(self, init_dict = None):
26
+ super().__init__(init_dict)
27
+
28
+ self._player = None
29
+ self._osc_route = None
30
+ self._offset_route = '/offset'
31
+
32
+ # self.OSC_DMXPLAYER_CONF[self._offset_route] = [ossia.ValueType.Float, None]
33
+
34
+
35
+ @property
36
+ def media(self):
37
+ return super().__getitem__('Media')
38
+
39
+ @media.setter
40
+ def media(self, media):
41
+ super().__setitem__('Media', media)
42
+
43
+ @property
44
+ def fadein_time(self):
45
+ return super().__getitem__('fadein_time')
46
+
47
+ @fadein_time.setter
48
+ def fadein_time(self, fadein_time):
49
+ super().__setitem__('fadein_time', fadein_time)
50
+
51
+ @property
52
+ def fadeout_time(self):
53
+ return super().__getitem__('fadeout_time')
54
+
55
+ @fadeout_time.setter
56
+ def fadeout_time(self, fadeout_time):
57
+ super().__setitem__('fadeout_time', fadeout_time)
58
+
59
+ def player(self, player):
60
+ self._player = player
61
+
62
+ def osc_route(self, osc_route):
63
+ self._osc_route = osc_route
64
+
65
+ def offset_route(self, offset_route):
66
+ self._offset_route = offset_route
67
+
68
+ def review_offset(self, timecode):
69
+ return -(float(timecode.milliseconds))
70
+
71
+ def arm(self, conf, ossia, armed_list, init = False):
72
+ self._conf = conf
73
+ self._armed_list = armed_list
74
+
75
+ if not self.enabled:
76
+ if self.loaded and self in self._armed_list:
77
+ self.disarm(ossia)
78
+ return False
79
+ elif self.loaded and not init:
80
+ if not self in self._armed_list:
81
+ self._armed_list.append(self)
82
+ return True
83
+
84
+ # Assign its own audioplayer object
85
+ # try:
86
+ # self._player = DmxPlayer(
87
+ # self._conf.players_port_index,
88
+ # self._conf.node_conf['dmxplayer']['path'],
89
+ # str(self._conf.node_conf['dmxplayer']['args']),
90
+ # str(
91
+ # path.join(
92
+ # self._conf.library_path,
93
+ # 'media',
94
+ # self.media['file_name']
95
+ # )
96
+ # )
97
+ # )
98
+ # except Exception as e:
99
+ # raise e
100
+
101
+ # self._player.start()
102
+
103
+ # And dinamically attach it to the ossia for remote control it
104
+ self._osc_route = f'/players/dmxplayer-{self.uuid}'
105
+
106
+ # ossia.add_player_nodes(
107
+ # PlayerOSCConfData(
108
+ # device_name=self._osc_route,
109
+ # host=self._conf.node_conf['osc_dest_host'],
110
+ # in_port=self._player.port,
111
+ # out_port=self._player.port + 1,
112
+ # dictionary=self.OSC_DMXPLAYER_CONF
113
+ # )
114
+ # )
115
+
116
+ self.loaded = True
117
+ if not self in self._armed_list:
118
+ self._armed_list.append(self)
119
+
120
+ if self.post_go == 'go' and self._target_object:
121
+ self._target_object.arm(self._conf, ossia, self._armed_list, init)
122
+
123
+ return True
124
+
125
+ @logged
126
+ def go(self, ossia, mtc):
127
+ if not self.loaded:
128
+ raise Exception(f'{self.__class__.__name__} {self.uuid} not loaded to go')
129
+ else:
130
+ # THREADED GO
131
+ thread = Thread(
132
+ name = f'GO:{self.__class__.__name__}:{self.uuid}',
133
+ target = self.go_thread,
134
+ args = [ossia, mtc]
135
+ )
136
+ thread.start()
137
+
138
+ def go_thread(self, ossia, mtc):
139
+ # ARM NEXT TARGET
140
+ if self._target_object:
141
+ self._target_object.arm(self._conf, ossia, self._armed_list)
142
+
143
+ # PREWAIT
144
+ if self.prewait > 0:
145
+ sleep(self.prewait.milliseconds / 1000)
146
+
147
+ # PLAY : specific DMX cue stuff
148
+ try:
149
+ key = f'{self._osc_route}{self._offset_route}'
150
+ ossia.osc_registered_nodes[key][0].value = self.review_offset(mtc)
151
+ Logger.log_info(
152
+ f"DMX play {self.uuid}: {key} {str(ossia.osc_registered_nodes[key][0].value)}",
153
+ extra = {"caller": self.__class__.__name__}
154
+ )
155
+ except KeyError:
156
+ Logger.log_debug(
157
+ f'OSC Key error 1 in go_callback {key}',
158
+ extra = {"caller": self.__class__.__name__}
159
+ )
160
+ try:
161
+ key = f'{self._osc_route}/mtcfollow'
162
+ ossia.osc_registered_nodes[key][0].value = True
163
+ except KeyError:
164
+ Logger.log_debug(
165
+ f'OSC Key error 2 in go_callback {key}',
166
+ extra = {"caller": self.__class__.__name__}
167
+ )
168
+
169
+ # POSTWAIT
170
+ if self.postwait > 0:
171
+ sleep(self.postwait.milliseconds / 1000)
172
+
173
+ # POST-GO GO
174
+ if self.post_go == 'go' and self._target_object:
175
+ self._target_object.go(ossia, mtc)
176
+
177
+ try:
178
+ while self._player.is_alive():
179
+ sleep(0.05)
180
+ except AttributeError:
181
+ return
182
+
183
+ if self in self._armed_list:
184
+ self.disarm(ossia)
185
+
186
+ def disarm(self, ossia):
187
+ if self.loaded is True:
188
+ try:
189
+ self._player.kill()
190
+ self._conf.players_port_index['used'].remove(self._player.port)
191
+ self._player.join()
192
+ self._player = None
193
+
194
+ # ossia.remove_nodes( OSCConfData(device_name=self._osc_route, dictionary = self.OSC_DMXPLAYER_CONF) )
195
+
196
+ except Exception as e:
197
+ Logger.log_warning(
198
+ f'Could not properly unload {self.uuid} : {e}',
199
+ extra = {"caller": self.__class__.__name__}
200
+ )
201
+
202
+ try:
203
+ if self in self._armed_list:
204
+ self._armed_list.remove(self)
205
+ except:
206
+ pass
207
+
208
+ self.loaded = False
209
+
210
+ return True
211
+ else:
212
+ return False
213
+
214
+ @property
215
+ def scene(self):
216
+ return self['dmx_scene']
217
+
218
+ @scene.setter
219
+ def scene(self, scene):
220
+ if isinstance(scene, DmxScene):
221
+ super().__setitem__('dmx_scene', scene)
222
+ elif isinstance(scene, dict):
223
+ super().__setitem__('dmx_scene', DmxScene(init_dict=scene))
224
+ else:
225
+ raise NotImplementedError
226
+
227
+ class DmxScene(dict):
228
+ def __init__(self, init_dict=None):
229
+ super().__init__()
230
+ if init_dict:
231
+ for k, v, in init_dict.items():
232
+ if isinstance(k, int):
233
+ super().__setitem__(k, DmxUniverse(v))
234
+ elif k == 'DmxUniverse':
235
+ for u in v:
236
+ super().__setitem__(u['id'], DmxUniverse(init_dict=u))
237
+
238
+ def universe(self, num=None):
239
+ if num is not None:
240
+ return super().__getitem__(num)
241
+
242
+ def universes(self):
243
+ return self
244
+
245
+ def set_universe(self, universe, num=0):
246
+ super().__setitem__(num, DmxUniverse(universe))
247
+
248
+
249
+
250
+ #merge two universes, priority on the newcoming
251
+ def merge_universe(self, universe, num=0):
252
+ super().__getitem__(num).update(universe)
253
+
254
+
255
+
256
+ class DmxUniverse(dict):
257
+
258
+ def __init__(self, init_dict=None):
259
+ super().__init__()
260
+ if init_dict:
261
+ for k, v, in init_dict.items():
262
+ if isinstance(k, int):
263
+ super().__setitem__(k, DmxChannel(v))
264
+ elif k == 'DmxChannel':
265
+ for u in v:
266
+ super().__setitem__(u['id'], DmxChannel(u['&']))
267
+
268
+
269
+
270
+ def channel(self, channel):
271
+ return super().__getitem__(channel)
272
+
273
+ def set_channel(self, channel, value):
274
+ if isinstance(value, DmxChannel):
275
+ super().__setitem__(channel, value)
276
+ else:
277
+ super().__setitem__(channel, DmxChannel(value))
278
+ return self
279
+
280
+ def setall(self, value):
281
+ for channel in range(512):
282
+ super().__setitem__(channel, value)
283
+ return self #TODO: valorate return self to be able to do things like 'universe_full = DmxUniverse().setall(255)'
284
+
285
+ def update(self, other=None, **kwargs):
286
+ if other is not None:
287
+ for k, v in other.items() if isinstance(other, Mapping) else other:
288
+ self[k] = DmxChannel(v)
289
+ for k, v in kwargs.items():
290
+ self[k] = DmxChannel(v)
291
+
292
+ class DmxChannel():
293
+ def __init__(self, value=None, init_dict = None):
294
+ self._value = value
295
+ if init_dict is not None:
296
+ self.value = init_dict
297
+
298
+ def __repr__(self):
299
+ return str(self.value)
300
+
301
+ @property
302
+ def value(self):
303
+ return self._value
304
+
305
+ @value.setter
306
+ def value (self, value):
307
+ if value > 255:
308
+ value = 255
309
+ self._value = value
@@ -0,0 +1,287 @@
1
+ from os import path
2
+ # from pyossia import ossia
3
+ from threading import Thread
4
+ from time import sleep
5
+
6
+ from Cue import Cue
7
+ from cuemsutils.CTimecode import CTimecode
8
+ # from ..players.VideoPlayer import VideoPlayer
9
+ # from ..OssiaServer import OssiaServer, OSCConfData, PlayerOSCConfData
10
+ from cuemsutils.log import logged, Logger
11
+ class VideoCue(Cue):
12
+ '''
13
+ OSC_VIDEOPLAYER_CONF = {'/jadeo/xscale' : [ossia.ValueType.Float, None],
14
+ '/jadeo/yscale' : [ossia.ValueType.Float, None],
15
+ '/jadeo/corners' : [ossia.ValueType.List, None],
16
+ '/jadeo/corner1' : [ossia.ValueType.List, None],
17
+ '/jadeo/corner2' : [ossia.ValueType.List, None],
18
+ '/jadeo/corner3' : [ossia.ValueType.List, None],
19
+ '/jadeo/corner4' : [ossia.ValueType.List, None],
20
+ '/jadeo/start' : [ossia.ValueType.Bool, None],
21
+ '/jadeo/load' : [ossia.ValueType.String, None],
22
+ '/jadeo/quit' : [ossia.ValueType.Bool, None],
23
+ '/jadeo/midi/connect' : [ossia.ValueType.String, None],
24
+ '/jadeo/midi/disconnect' : [ossia.ValueType.Impulse, None]
25
+ }
26
+ '''
27
+
28
+ def __init__(self, init_dict = None):
29
+ super().__init__(init_dict)
30
+
31
+ self._player = None
32
+ self._osc_route = None
33
+ self._go_thread = None
34
+
35
+ # TODO: Adjust framerates for universal use, by now 25 fps for video
36
+ self._start_mtc = CTimecode(framerate=25)
37
+ self._end_mtc = CTimecode(framerate=25)
38
+
39
+ '''
40
+ self.OSC_VIDEOPLAYER_CONF['/jadeo/offset'] = [ossia.ValueType.String, None]
41
+ self.OSC_VIDEOPLAYER_CONF['/jadeo/offset'] = [ossia.ValueType.Int, None]
42
+ '''
43
+
44
+ @property
45
+ def media(self):
46
+ return super().__getitem__('Media')
47
+
48
+ @media.setter
49
+ def media(self, media):
50
+ super().__setitem__('Media', media)
51
+
52
+ @property
53
+ def outputs(self):
54
+ return super().__getitem__('Outputs')
55
+
56
+ @outputs.setter
57
+ def outputs(self, outputs):
58
+ super().__setitem__('Outputs', outputs)
59
+
60
+ def player(self, player):
61
+ self._player = player
62
+
63
+ def osc_route(self, osc_route):
64
+ self._osc_route = osc_route
65
+
66
+ def arm(self, conf, ossia, armed_list, init = False):
67
+ self._conf = conf
68
+ self._armed_list = armed_list
69
+
70
+ if not self.enabled:
71
+ if self.loaded and self in self._armed_list:
72
+ self.disarm(ossia)
73
+ return False
74
+ elif self.loaded and not init:
75
+ if not self in self._armed_list:
76
+ self._armed_list.append(self)
77
+ return True
78
+
79
+ if self._local:
80
+ try:
81
+ key = f'{self._osc_route}/jadeo/cmd'
82
+ ossia.send_message(key, 'midi disconnect')
83
+ Logger.log_info(
84
+ key + " " + str(ossia._oscquery_registered_nodes[key][0].value),
85
+ extra = {"caller": self.__class__.__name__}
86
+ )
87
+ except KeyError:
88
+ Logger.log_debug(
89
+ f'Key error 1 (disconnect) in arm_callback {key}',
90
+ extra = {"caller": self.__class__.__name__}
91
+ )
92
+
93
+ try:
94
+ key = f'{self._osc_route}/jadeo/load'
95
+ value = str(path.join(self._conf.library_path, 'media', self.media.file_name))
96
+ ossia.send_message(key, value)
97
+ Logger.log_info(
98
+ key + " " + str(ossia._oscquery_registered_nodes[key][0].value),
99
+ extra = {"caller": self.__class__.__name__}
100
+ )
101
+ except KeyError:
102
+ Logger.log_debug(
103
+ f'Key error 2 (load) in arm_callback {key}',
104
+ extra = {"caller": self.__class__.__name__}
105
+ )
106
+
107
+ self.loaded = True
108
+ if not self in self._armed_list:
109
+ self._armed_list.append(self)
110
+
111
+ if self.post_go == 'go' and self._target_object:
112
+ self._target_object.arm(self._conf, ossia, self._armed_list, init)
113
+
114
+ return True
115
+
116
+ @logged
117
+ def go(self, ossia, mtc):
118
+ if not self.loaded:
119
+ raise Exception(f'{self.__class__.__name__} {self.uuid} not loaded to go')
120
+ else:
121
+ # THREADED GO
122
+ self._go_thread = Thread(name = f'GO:{self.__class__.__name__}:{self.uuid}', target = self.go_thread_func, args = [ossia, mtc])
123
+ self._go_thread.start()
124
+
125
+ def go_thread_func(self, ossia, mtc):
126
+ # ARM NEXT TARGET
127
+ if self._target_object:
128
+ self._target_object.arm(self._conf, ossia, self._armed_list)
129
+
130
+ # PREWAIT
131
+ if self.prewait > 0:
132
+ sleep(self.prewait.milliseconds / 1000)
133
+
134
+ ### harcoded for TODO: proto_fruta, need fixx
135
+ #try to make all cues start at sync at 10 second timecode!
136
+ harcoded_go_offset = 20000
137
+
138
+
139
+ if self._local:
140
+ # PLAY : specific video cue stuff
141
+ try:
142
+ key = f'{self._osc_route}/jadeo/offset'
143
+ #self._start_mtc = mtc.main_tc
144
+
145
+ ### harcoded for TODO: proto_fruta, need fixx
146
+ self._start_mtc = CTimecode(frames=harcoded_go_offset)
147
+
148
+
149
+ duration = self.media.regions[0].out_time - self.media.regions[0].in_time
150
+ duration = duration.return_in_other_framerate(mtc.main_tc.framerate)
151
+ self._end_mtc = self._start_mtc + duration
152
+ cue_in_time_fr_adjusted = self.media.regions[0].in_time.return_in_other_framerate(mtc.main_tc.framerate)
153
+ offset_to_go = cue_in_time_fr_adjusted.frame_number - self._start_mtc.frame_number
154
+ # ossia._oscquery_registered_nodes[key][0].value = offset_to_go
155
+ ossia.send_message(key, offset_to_go)
156
+ Logger.log_info(
157
+ key + " " + str(ossia._oscquery_registered_nodes[key][0].value),
158
+ extra = {"caller": self.__class__.__name__}
159
+ )
160
+ except KeyError:
161
+ Logger.log_debug(
162
+ f'Key error 1 (offset) in go_callback {key}',
163
+ extra = {"caller": self.__class__.__name__}
164
+ )
165
+
166
+ try:
167
+ key = f'{self._osc_route}/jadeo/cmd'
168
+ ossia.send_message(key, "midi connect Midi Through")
169
+ except KeyError:
170
+ Logger.log_debug(
171
+ f'Key error 2 (connect) in go_callback {key}',
172
+ extra = {"caller": self.__class__.__name__}
173
+ )
174
+
175
+ # POSTWAIT
176
+ if self.postwait > 0:
177
+ sleep(self.postwait.milliseconds / 1000)
178
+
179
+ if self.post_go == 'go' and self._target_object:
180
+ self._target_object.go(ossia, mtc)
181
+
182
+ try:
183
+ loop_counter = 0
184
+ duration = self.media.regions[0].out_time - self.media.regions[0].in_time
185
+ duration = duration.return_in_other_framerate(mtc.main_tc.framerate)
186
+ in_time_adjusted = self.media.regions[0].in_time.return_in_other_framerate(mtc.main_tc.framerate)
187
+
188
+ while not self.media.regions[0].loop or loop_counter < self.media.regions[0].loop:
189
+ while mtc.main_tc.milliseconds < self._end_mtc.milliseconds:
190
+ sleep(0.005)
191
+
192
+ if self._local:
193
+ try:
194
+ key = f'{self._osc_route}/jadeo/offset'
195
+ self._start_mtc = mtc.main_tc
196
+ self._end_mtc = self._start_mtc + duration
197
+ offset_to_go = in_time_adjusted.frame_number - self._start_mtc.frame_number
198
+ ossia.send_message(key, offset_to_go)
199
+ Logger.log_info(
200
+ key + " " + str(ossia._oscquery_registered_nodes[key][0].value),
201
+ extra = {"caller": self.__class__.__name__}
202
+ )
203
+ except KeyError:
204
+ Logger.log_debug(
205
+ f'Key error 1 (offset) in go_callback {key}',
206
+ extra = {"caller": self.__class__.__name__}
207
+ )
208
+
209
+ loop_counter += 1
210
+
211
+ if self._local:
212
+ try:
213
+ key = f'{self._osc_route}/jadeo/cmd'
214
+ ossia.send_message(key, 'midi disconnect')
215
+ Logger.log_info(
216
+ key + " " + str(ossia._oscquery_registered_nodes[key][0].value),
217
+ extra = {"caller": self.__class__.__name__}
218
+ )
219
+ except KeyError:
220
+ Logger.log_debug(
221
+ f'Key error 1 (disconnect) in arm_callback {key}',
222
+ extra = {"caller": self.__class__.__name__}
223
+ )
224
+
225
+ except AttributeError:
226
+ pass
227
+
228
+ if self in self._armed_list:
229
+ self.disarm(ossia)
230
+
231
+ def disarm(self, ossia = None):
232
+ if self.loaded is True:
233
+ '''
234
+ # Needed when each cue launched its own player
235
+ try:
236
+ self._player.kill()
237
+ self._conf.osc_port_index['used'].remove(self._player.port)
238
+ self._player.join()
239
+ self._player = None
240
+
241
+ ossia.remove_nodes( OSCConfData(device_name=self._osc_route, dictionary = self.OSC_VIDEOPLAYER_CONF) )
242
+
243
+ except Exception as e:
244
+ logger.warning(f'Could not properly unload {self.__class__.__name__} {self.uuid} : {e}')
245
+ '''
246
+
247
+ try:
248
+ if self in self._armed_list:
249
+ self._armed_list.remove(self)
250
+ except:
251
+ pass
252
+
253
+ self.loaded = False
254
+
255
+ return True
256
+ else:
257
+ return False
258
+
259
+ def stop(self):
260
+ self._stop_requested = True
261
+
262
+ def check_mappings(self, settings):
263
+ if not settings.project_node_mappings:
264
+ return True
265
+
266
+ found = True
267
+
268
+ map_list = ['default']
269
+
270
+ if settings.project_node_mappings['video']['outputs']:
271
+ for elem in settings.project_node_mappings['video']['outputs']:
272
+ for map in elem['mappings']:
273
+ map_list.append(map['mapped_to'])
274
+
275
+ for output in self.outputs:
276
+ # if output['node_uuid'] == settings.node_conf['uuid']:
277
+
278
+ if output['output_name'][:36] == settings.node_conf['uuid']:
279
+ self._local = True
280
+ if output['output_name'][37:] not in map_list:
281
+ found = False
282
+ break
283
+ else:
284
+ self._local = False
285
+ found = True
286
+
287
+ return found
File without changes
cuemsutils/log.py ADDED
@@ -0,0 +1,77 @@
1
+ import sys
2
+ from logging import getLogger, StreamHandler, Formatter, DEBUG, INFO, ERROR, WARNING
3
+ from logging.handlers import SysLogHandler
4
+ from functools import wraps
5
+
6
+ cuemsFormatter = Formatter('[%(asctime)s][%(levelname)s] \tFormitGo (PID: %(process)d)-(%(threadName)-9s)-(%(name)s:%(funcName)s:%(caller)s)> %(message)s')
7
+
8
+ def root_logger(with_syslog = True, with_stdout = True):
9
+ """
10
+ Create a root logger with a custom formatter.
11
+ """
12
+ logger = getLogger()
13
+ logger.setLevel(DEBUG)
14
+
15
+ if with_stdout:
16
+ sh = StreamHandler(sys.stdout)
17
+ sh.setFormatter(cuemsFormatter)
18
+ logger.addHandler(sh)
19
+
20
+ if with_syslog:
21
+ syslog_handler = SysLogHandler(
22
+ address = '/dev/log', facility = 'local0'
23
+ )
24
+ syslog_handler.setFormatter(cuemsFormatter)
25
+ logger.addHandler(syslog_handler)
26
+
27
+ return logger
28
+
29
+ class Logger:
30
+ logger = root_logger()
31
+
32
+ @staticmethod
33
+ def log(level, message, extra):
34
+ Logger.logger.log(level, message, stacklevel = 4, extra = extra)
35
+
36
+ @staticmethod
37
+ def log_debug(message, extra = {"caller": ''}):
38
+ Logger.log(DEBUG, message, extra = extra)
39
+
40
+ @staticmethod
41
+ def log_info(message, extra = {"caller": ''}):
42
+ Logger.log(INFO, message, extra = extra)
43
+
44
+ @staticmethod
45
+ def log_error(message, extra = {"caller": ''}):
46
+ Logger.log(ERROR, message, extra = extra)
47
+
48
+ @staticmethod
49
+ def log_warning(message, extra = {"caller": ''}):
50
+ Logger.log(WARNING, message, extra = extra)
51
+
52
+ def logged(func):
53
+ """
54
+ A decorator function to log information about function calls and their results.
55
+ """
56
+
57
+ @wraps(func)
58
+ def wrapper(*args, **kwargs):
59
+ """
60
+ The wrapper function that logs function calls and their results.
61
+ """
62
+ d = {"caller": func.__name__}
63
+ Logger.logger = getLogger(func.__module__)
64
+ Logger.log_info(f"Call recieved", extra = d)
65
+ Logger.log_debug(f"Using args: {args} and kwargs: {kwargs}", extra = d)
66
+ try:
67
+ result = func(*args, **kwargs)
68
+ Logger.log_debug(f"Finished with result: {result}", extra = d)
69
+
70
+ except Exception as e:
71
+ Logger.log_error(f"Error occurred: {e}", extra = d)
72
+ raise
73
+
74
+ else:
75
+ return result
76
+
77
+ return wrapper
@@ -0,0 +1,21 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <xs:schema targetNamespace="http://stagelab.net/cuems" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:cms="http://stagelab.net/cuems">
3
+ <xs:element name="CuemsOutputs">
4
+ <xs:complexType>
5
+ <xs:sequence>
6
+ <xs:element name="video_outputs" type="cms:OutputsType" />
7
+ <xs:element name="default_video_output" type="xs:string" />
8
+ <xs:element name="audio_outputs" type="cms:OutputsType" />
9
+ <xs:element name="default_audio_output" type="xs:string" />
10
+ </xs:sequence>
11
+ </xs:complexType>
12
+ </xs:element>
13
+
14
+ <!-- TYPES DEFINITIONS -->
15
+ <xs:complexType name="OutputsType">
16
+ <xs:sequence>
17
+ <xs:element name="output" type="xs:string" minOccurs="0" maxOccurs="unbounded" />
18
+ </xs:sequence>
19
+ </xs:complexType>
20
+
21
+ </xs:schema>