print(data) def callback_function(error, result): print("callback") if error: print(error) return print(result) client = MeteorClient("ws://127.0.0.1:3000/websocket") client.on("connected", connected) client.on("failed", failed) client.connect() timestamp = str(datetime.now()) lat = 1.2956 lng = 103.7767 speed = 50 bearing = 50 altitude = 50 client.insert( "Telemetry", {"timestamp": timestamp, "lat": lat, "lng": lng, "speed": speed, "bearing": bearing, "altitude": altitude}, callback=insert_callback, )
class MWorker: multiprocessPluginManager = "" # yapsy manager meteorClient = "" # python-meteor client # session=requests.Session() #http client meteorUrl = "ws://127.0.0.1:3000/websocket" CYCLE_DEFAULT_DELAY = 20 # 10 seconds SYNCHRO_TIMEOUT = 10 # meteor functions are asynchron. this is delay for how long i should wait till # it is considered as fail... 2 sec are default subscription_status = {} # this will assure, subscription is ready... "subscription_name":true/false workername = "localhostworker" # this is worker name - this must be initialised at moment of start, and # must by uniqued... data = "" error = "" syncwaitdone = False def __init__(self, meteorUrl): """ constructor :return: """ self.meteorUrl = meteorUrl self.logm("MWorker:constructor", "python meteor client initialisation:") self.initMeteorConnect() self.logm("MWorker:constructor", "getting unassigned") # self.infiniteCycle() self.getUnassigned() def infiniteCycle(self): """ this is to keep thread running - it can be interupted by ctrl+c :return: """ while True: try: time.sleep(1) except KeyboardInterrupt: break def initMeteorConnect(self): """ this will use library python-meteor in order to escablish session to selected meteor :return: """ self.logm("MWorkerLib:initMeteorConnect:", "meteor python lib client iniciaisation...") self.meteorClient = MeteorClient('ws://127.0.0.1:3000/websocket', auto_reconnect=True) self.meteorClient.connect() self.subscribeCollectionSynchron(['unassigned', 'active', 'trash'], self.SYNCHRO_TIMEOUT) self.logm("MWorkerLib:initMeteorConnect:", "meteor python lib client iniciaisation done...") def meteorCollectionSubscribed(self, subscription): self.subscription_status[subscription] = True; self.logm("MWorkerLib:subscribed:", 'SUBSCRIBED {}'.format(subscription)) def meteorConnected(self): self.logm("MWorkerLib:connected:", ' CONNECTED') def initYapsy(self): """ inicialisation of yapsi subsystems... :return: """ self.multiprocessPluginManager = MultiprocessPluginManager() self.multiprocessPluginManager.setPluginPlaces(["plugins"]) self.multiprocessPluginManager.collectPlugins() self.logm("MWorkerLib:initYapsy:", "following pluggins ready") for pluginInfo in self.multiprocessPluginManager.getAllPlugins(): self.logm("MWorkerLib:initYapsy:", ">" + pluginInfo.name) def isSubscriptionProcessDone(self): """ this will check, if in self.subscription_status all collections are set to true :return: true if all requested collections are subscribed, orthervice false.. """ kk = self.subscription_status.keys() for col in kk: if self.subscription_status[col] == False: self.logm("MWorkerLib:isSubscriptionProcessDone:", self.subscription_status) return False return True def subscribeCollectionSynchron(self, collections, timeout): """ this is synchron method. this means, there is loop for "timeout" seconds, so subscription can be estalished... :param: timeout - number of seconds for cycle this function will have :param: collections - array of collections to be subscribed with self.meteorClient... ['unassigned', 'active', 'trash'] :return: will return true, or collection if some is false """ self.logm("MWorkerLib:subscribeCollectionsynchron:", "begining with subscription") self.meteorClient.on('subscribed', self.meteorCollectionSubscribed) self.meteorClient.on('connected', self.meteorConnected) for col in collections: self.subscription_status[col] = False self.meteorClient.subscribe(col) self.logm("MWorkerLib:subscribeCollectionSynchron", "subscription init done, now waiting...:" + str(self.meteorClient.subscriptions.keys())) epoch = int(time.time()) while (((int(time.time()) - epoch) < timeout) and ( (self.isSubscriptionProcessDone() == False))): try: time.sleep(1) self.logm("MWorkerLib:subscribeCollectionSynchron", "inside waiting cycle :" + str(self.meteorClient.subscriptions.keys())) except KeyboardInterrupt: break if (self.isSubscriptionProcessDone() == False): self.logm("MWorkerLib:subscribeCollectionSynchron", "some requested subscription failed...:" + str(self.subscription_status)) return False else: self.logm("MWorkerLib:subscribeCollectionSynchron", "requested subscription successfuly subscribed...") return True def getUnassigned(self): """ get list of unassigned tasks :return: list of unassigned tasks """ self.logm("MWorkerLib:getUnassigned", "geting all unassigned") all_posts = self.meteorClient.find('unassigned') self.logm("MWorkerLib:getUnassigned", "found following unassigned:" + str(all_posts)) def subscription_callback(self, error): if error: self.logm("MWorkerLib:subscription_callback", "subsribing to unassigned failed") return self.logm("MWorkerLib:subscription_callback", "subsribing to unassigned succeed") def logm(self, tag="undefined tag", text="undefined text"): """ logger wrapper .... :param tag: tag :param level: level ( 1..10 ) :param text: dscription of logging :return: """ timestamp = self.getTimestamp() print("{d} t:{a} t:{c} ".format(a=tag, c=text, d=timestamp)) ####################################### support methods def getTimestamp(self): ts = time.time() return datetime.datetime.fromtimestamp(ts).strftime('%Y-%m-%d %H:%M:%S') def callViewToolkit(self, method, parameters, callback): """ this will execute call... please note, for simple geting collection data, no need to create method... :param method: name of function :param parameters: what to give to viewtoolkit :callback - as all meteor calls are async, callback is required def callback_function(error, result): if error: print(error) return print(result) :return: """ self.logm("MWorkerLib:callViewToolkit", "method:" + method + " with parameters:" + parameters + " called") self.meteorClient.call(method, parameters, callback) def isPluginActiveInPluginManager(self, pluginname): """ this is to check if plugin which is parameter exist in worker, and if yes, if it is activated... :param pluginname: :return: true in case plugin is activated """ for pluginInfo in self.multiprocessPluginManager.getAllPlugins(): self.logm("MWorkerLib:isPlugginActiveInPluginManager:", "checking plugin " + pluginInfo.name + "against " + pluginname) if (pluginname == pluginInfo.name): self.logm("MWorkerLib:isPlugginActiveInPluginManager:", "plugin was found, let see if it is activated " + str(pluginInfo.is_activated)) if pluginInfo.is_activated: self.logm("MWorkerLib:isPlugginActiveInPluginManager:", "yes, plugin exist and was activated. returning true") return True self.logm("MWorkerLib:isPlugginActiveInPluginManager:", "could not found plugin you uare asking for, or it is not activated, returning False") return False def activatePlugin(self, pluginname): """ plugin activation itself :param pluginname: :return: """ self.logm("MWorkerLib:isPlugginActiveInPluginManager:activatePlugin", "plugin {a} will be activated now...".format(a=pluginname)) self.multiprocessPluginManager.activatePluginByName(pluginname) self.logm("MWorkerLib:isPlugginActiveInPluginManager:activatePlugin", "plugin {a} was activated...".format(a=pluginname)) def moveJobFromActiveToTrash(self,jobname): """ will move job by its name from active to trash :param jobname: :return: """ #kontrola, zda trash ma v sobe vytvoreneho workera: if(self.isWorkerInTrash()==False): self.createWorkerRecordInTrash() job=self.getJobInActiveByName(jobname) if (job==None): return False if self.createJobInTrashSynchronised(job): self.deleteJobFromActiveSynchronised(jobname) def deleteJobFromActiveSynchronised(self,jobname): """ delete job from active :param jobname: :return: """ result = False self.syncwaitdone = False # zacatek locku self.logm("MWorkerLib:deleteJobFromActiveSynchronised", "worker:" + self.workername + " is going to remove job from active " + str(jobname)) self.meteorClient.remove( 'active',{'_id':self.workername,'jobs._id':jobname}, callback=self.callback_synchro) res = self.syncWait() # konec locku self.logm("MWorkerLib:deleteJobFromActiveSynchronised", "worker:" + self.workername + " removed " + str(jobname)+ "result is "+str(res)) return res def createJobInTrashSynchronised(self,job): """ this will create job in trash collection under worker name... :param job: :return: """ #inseritng job self.logm("MWorkerLib:createJobInTrash", "worker:" + self.workername + " is going to insert to trash following: " + str(job)) self.syncwaitdone = False # zacatek locku self.meteorClient.update('trash', {'_id': self.workername}, {'$push': {"jobs":job}}, callback=self.callback_synchro) res = self.syncWait() # konec locku return res def getJobInActiveByName(self,jobname): """ this will get job back :param jobname: name of job in active ... :return: return job, or None """ res = self.meteorClient.find_one('active',{'_id':self.workername,'jobs.jobname':jobname}) if res==[]: return None return res def moveJobFromUnassignedToActiveSynchronised(self, jobname): """ this will shift job from unassigned to active... based on job name, which must be unique in hwhile system :param jobname: :return: """ #kontrola, zda existuje worker v active, a kdyz ne, tak se vlozi podle template if(self.isWorkerInActive()==False): self.logm("MWorkerLib:moveJobFromUnassignedToActiveSynchronised", " dont see record for worker {w} in active !! creating it ".format(w=self.workername)) self.createWorkerRecordInActive() resulttmp = self.findJobinUnassigned(jobname) if(len(resulttmp)!=1): raise NoJobFound(" given job name "+jobname+" doesnt exist") job=resulttmp[0] self.logm("MWorkerLib:moveJobFromUnassignedToActiveSynchronised", " job {a} will be activated now...".format(a=str(job))) if (self.createMyJobInActiveSynchronised(job)): if(self.deleteJobFromUnassignedSynchronised(job)): self.logm("MWorkerLib:moveJobFromUnassignedToActiveSynchronised", " job inserting to db is ok, job deleted from unassigned ") return True self.logm("MWorkerLib:moveJobFromUnassignedToActiveSynchronised", "job inserting to db failed") return False def isWorkerInTrash(self): """ will check is self.workername can be found in trash :return: true if it can be found, false if not """ result = self.meteorClient.find('trash',{"_id":self.workername}) self.logm("MWorkerLib:isWorkerInTrash", "checking, if i see my worker name in trash "+str(result)) if len(result)==1: self.logm("MWorkerLib:isWorkerInTrash", " worker with my name found in trash, erturning true") return True else: self.logm("MWorkerLib:isWorkerInTrash", "found nothing, returning False") return False def isWorkerInActive(self): """ will check is self.workername can be found in active :return: true if it can be found, false if not """ result = self.meteorClient.find('active',{"_id":self.workername}) self.logm("MWorkerLib:isWorkerInActive", "checking, if i see my worker name in active "+str(result)) if len(result)==1: self.logm("MWorkerLib:isWorkerInActive", " worker with my name found in active, erturning true") return True else: self.logm("MWorkerLib:isWorkerInActive", "found nothing, returning False") return False def createWorkerRecordInActive(self): """ will create new worker - it will not be checking if there is already worker created in active... :return: """ template={"_id":self.workername,"worker":self.workername,"workerusername":"******","jobs":[]} self.logm("MWorkerLib:createWorkerRecordInActive", "worker:" + self.workername + " is going to by added to collection active ") self.syncwaitdone = False # zacatek locku self.meteorClient.insert('active', template, callback=self.callback_synchro) res = self.syncWait() # konec locku return res def createWorkerRecordInTrash(self): """ will create new worker - it will not be checking if there is already worker created in active... :return: """ template={"_id":self.workername,"worker":self.workername,"workerusername":"******","jobs":[]} self.logm("MWorkerLib:createWorkerRecordInTrash", "worker:" + self.workername + " is going to by added to collection trash ") self.syncwaitdone = False # zacatek locku self.meteorClient.insert('trash', template, callback=self.callback_synchro) res = self.syncWait() # konec locku return res def findJobinUnassigned(self, jobname): """ will return job from unassigned by jobname this is synchron method... it will wait for ( synchron_timeout ) seconds, and return false :param job: :return: false on timeout ( synchron_timeout ) or if return is different from 1. """ return self.meteorClient.find('unassigned', {"_id": jobname}) def createMyJobInActiveSynchronised(self, job): """ this will take as input dict of job... and add it to jobs[] in proper worker... :param job: dict of job... :return: false on timeout ( synchron_timeout ) or if return is different from 1. """ template = { 'pluginname': job['pluginname'], 'jobname': job['jobname'], 'starttime': int(time.time()), # epocha 'ttl': job['expectedttl'], 'inputparams': job['inputparams'], 'progress': 0, 'result': "undefined" } self.logm("MWorkerLib:createMyJobInActive", "worker:" + self.workername + " is going to insert to active following: " + str(job)) self.syncwaitdone = False # zacatek locku self.meteorClient.update('active', {'_id': self.workername}, {'$push': {'jobs': template}}, callback=self.callback_synchro) res = self.syncWait() # konec locku return res def callback_synchro(self, error, data): self.data = data self.error = error if error: self.logm("MWorkerLib:update_callback_synchro", "worker:" + self.workername + " failed error is " + str(error)) self.syncwaitdone = True return self.logm("MWorkerLib:update_callback_synchro", "is ok. number of updates is " + str(data)) self.syncwaitdone = True def syncWait(self): """ this will wait till data of error are not empty. it will do for self.SYNCHRO_TIMEOUT seconds. then it will return False also it will check what is return value from request. if update is 0, false, othervice true """ epoch = int(time.time()) while (((int(time.time()) - epoch) < self.SYNCHRO_TIMEOUT) and (self.syncwaitdone == False)): try: time.sleep(1) self.logm("MWorkerLib:syncWait", "inside waiting cycle :") except KeyboardInterrupt: break if (self.syncwaitdone == False): # cycle was broken before timeou return False try: # zkouska, zda se updatoval aspon jeden radek if (int(self.data) == 0): # mongo changed nothing, but there was no error return False else: return True except: pass # nothing found, cykluus end on timeout return False def deleteJobFromUnassignedSynchronised(self, job): """ this will delete selected job from unassigned this is synchron method... it will wait for ( synchron_timeout ) seconds, and return false :param job: :return: false on timeout ( synchron_timeout ) or if return is different from 1. """ self.syncwaitdone = False # zacatek locku self.logm("MWorkerLib:createMyJobInActive", "worker:" + self.workername + " is going to remove job from unassigned " + str(job)) self.meteorClient.remove('unassigned', {'_id': job['_id']}, callback=self.callback_synchro) res = self.syncWait() # konec locku self.logm("MWorkerLib:createMyJobInActive", "worker:" + self.workername + " removed " + str(job)+ "result is "+str(res)) return res #########################testing def testScenario1(self): """ this is scenario 1... this will move job from unassigned to active, then from active to trash... also it will list unassigned tasks :return: self.logm("MWorkerLib:testScenario1", "start test scenario 1") self.logm("MWorkerLib:testScenario1", "moving from unassigned to active") all_posts = self.meteorClient.find('unassigned') #job = (all_posts).keys() #get first unassigned: firstUnassigned=self.meteorClient.find('unassigned')[0] ttltmp=100 #pro testovaci ucely jsem vytvoril v active workera: # db.active.insert({"_id":"localhostworker","worker":"localhostworker","workerusername":"******","jobs":[]}) template={ 'pluginname':firstUnassigned['pluginname'], 'starttime':int(time.time()), #epocha 'ttl':ttltmp, 'inputparams':firstUnassigned['inputparams'], 'progress':0, 'result':"undefined" } #insert to active: #musim pouzit id_ protoze klient rve ze kod je insecure a musi se pouzit id #s _id je dalsi problem. protoze interne mongodb pouziva objekt, ne stringu #tak bude vyhodnejsi nahradit pri vytvoreni workeru v active _id s nejakym retezcem _ worker=self.meteorClient.find_one('active',{"worker" : "localhostworker"}) #worker["workerusername"]="******" self.logm("MWorkerLib:testScenario1", "worker:"+str(worker)+" "+worker['_id']) worker1=self.meteorClient.find_one('active',{"_id":worker["_id"]}) self.logm("MWorkerLib:testScenario1", "worker:"+str(worker1)+" "+worker1['_id']) self.meteorClient.update('active',{'_id':worker['_id']},{'$push':{'jobs':template}},callback=update_callback) #self.infiniteCycle() self.logm("MWorkerLib:testScenario1", "deleting job from unassigned") self.meteorClient.remove('unassigned',{'_id':firstUnassigned['_id']},callback=remove_callback) time.sleep(5) """ job = {'_id': "asdasdsad", 'jobname': "asdasd", 'pluginname': "asdasda", 'inputparams': "assad", 'expectedttl': "100"} #otestovane a funkcni 11.2.2016: #tohle presune job s danym jmenem do active pod jmeno mistniho workera... #self.moveJobFromUnassignedToActiveSynchronised("blabla") self.moveJobFromActiveToTrash("blabla")
class DDP(Thread): def __init__(self): Thread.__init__(self) self.meteor = conf.get('ddp', 'meteor') self.client = MeteorClient(self.meteor, debug=False) self.client.on('added', self.on_added) self.client.on('changed', self.on_changed) self.client.on('subscribed', self.on_subscribed) self.client.on('connected', self.on_connected) self.client.on('removed', self.on_removed) self.client.on('closed', self.on_closed) self.client.on('logged_in', self.on_logged_in) self.displayName = conf.get('ddp', 'room_name') self.vu_min = -50 self.vu_range = 50 self.vu_data = 0 self.last_vu = None self.ip = conf.get('ingest', 'address') self.id = conf.get('ingest', 'hostname') self._user = conf.get('ddp', 'user') self._password = conf.get('ddp', 'password') self._http_host = conf.get('ddp', 'http_host') self._audiostream_port = conf.get('audiostream', 'port') or 31337 self.store_audio = conf.get_boolean('ddp', 'store_audio') self.screenshot_file = conf.get('ddp', 'existing_screenshot') self.high_quality = conf.get_boolean('ddp', 'hq_snapshot') self.paused = False self.recording = False self.currentMediaPackage = None self.currentProfile = None self.has_disconnected = False screen = Gdk.Screen.get_default() self._screen_width = screen.get_width() self._screen_height = screen.get_height() self.cardindex = None cam_available = conf.get('ddp', 'cam_available') or 0 if cam_available in ('True', 'true', True, '1', 1): self.cam_available = 1 elif cam_available in ('False', 'false', False, '0', 0): self.cam_available = 0 else: self.cam_available = int(cam_available) # Getting audiostream params. either using existing audiostreaming server like icecast or the audiostream plugin if conf.get('ddp', 'existing_stream_host'): self._stream_host = conf.get('ddp', 'existing_stream_host') else: self._stream_host = self.ip if conf.get_int('ddp', 'existing_stream_port'): self._audiostream_port = conf.get_int('ddp', 'existing_stream_port') else: self._audiostream_port = conf.get_int('audiostream', 'port') or 31337 if conf.get('ddp', 'existing_stream_key'): self.stream_key = conf.get('ddp', 'existing_stream_key') else: self.stream_key = uuid.uuid4().get_hex() if conf.get('ddp', 'extra_params'): self.extra_params_list = conf.get('ddp', 'extra_params').split(';') else: self.extra_params_list = [] logger.info( 'audiostream URI: {}'.format('http://' + self._stream_host + ':' + str(self._audiostream_port) + '/' + self.stream_key)) dispatcher.connect('init', self.on_init) dispatcher.connect('recorder-vumeter', self.vumeter) dispatcher.connect('timer-short', self.update_vu) dispatcher.connect('timer-short', self.heartbeat) dispatcher.connect('recorder-started', self.on_start_recording) dispatcher.connect('recorder-stopped', self.on_stop_recording) dispatcher.connect('recorder-status', self.on_rec_status_update) def run(self): self.connect() def connect(self): if not self.has_disconnected: try: self.client.connect() except Exception: logger.warn('DDP connection failed') def update(self, collection, query, update): if self.client.connected and self.subscribedTo('GalicasterControl'): try: self.client.update(collection, query, update, callback=self.update_callback) except Exception: logger.warn("Error updating document " "{collection: %s, query: %s, update: %s}" % (collection, query, update)) def insert(self, collection, document): if self.client.connected and self.subscribedTo('GalicasterControl'): try: self.client.insert(collection, document, callback=self.insert_callback) except Exception: logger.warn( "Error inserting document {collection: %s, document: %s}" % (collection, document)) def heartbeat(self, element): if self.client.connected: self.update_images() else: self.connect() def on_start_recording(self, sender, id): self.recording = True self.currentMediaPackage = self.media_package_metadata(id) self.currentProfile = conf.get_current_profile().name self.update('rooms', {'_id': self.id}, { '$set': { 'currentMediaPackage': self.currentMediaPackage, 'currentProfile': self.currentProfile, 'recording': self.recording } }) def on_stop_recording(self, mpid, sender=None): self.recording = False self.currentMediaPackage = None self.currentProfile = None self.update('rooms', {'_id': self.id}, { '$unset': { 'currentMediaPackage': '', 'currentProfile': '' }, '$set': { 'recording': self.recording } }) self.update_images(1.5) def on_init(self, data): self.update_images(1.5) def update_images(self, delay=0.0): worker = Thread(target=self._update_images, args=(delay, )) worker.start() def _update_images(self, delay): time.sleep(delay) files = {} if not self.screenshot_file: # take a screenshot with pyscreenshot im = ImageGrab.grab(bbox=(0, 0, self._screen_width, self._screen_height), backend='imagemagick') else: try: # used if screenshot already exists im = Image.open(self.screenshot_file) except IOError as e: logger.warn("Unable to open screenshot file {0}".format( self.screenshot_file)) return output = cStringIO.StringIO() image_format = 'JPEG' if not self.high_quality: im.thumbnail((640, 360), Image.ANTIALIAS) else: image_format = 'PNG' if im.mode != "RGB": im = im.convert("RGB") im.save(output, format=image_format ) # to reduce jpeg size use param: optimize=True files['galicaster'] = ('galicaster.jpg', output.getvalue(), 'image/jpeg') try: # add verify=False for testing self signed certs requests.post( "%s/image/%s" % (self._http_host, self.id), files=files, auth=(self._user, self._password )) # to ignore ssl verification, use param: verify=False except Exception: logger.warn('Unable to post images') def vumeter(self, element, data, data_chan2, vu_bool): if data == "Inf": data = 0 else: if data < -self.vu_range: data = -self.vu_range elif data > 0: data = 0 self.vu_data = int( ((data + self.vu_range) / float(self.vu_range)) * 100) def update_vu(self, element): if self.vu_data != self.last_vu: update = {'vumeter': self.vu_data} self.update('rooms', {'_id': self.id}, {'$set': update}) self.last_vu = self.vu_data def on_rec_status_update(self, element, data): if data == 'paused': is_paused = True else: is_paused = False if is_paused: self.update_images(.75) if self.paused == is_paused: self.update('rooms', {'_id': self.id}, {'$set': { 'paused': is_paused }}) self.paused = is_paused if data == 'recording': self.update_images(.75) def media_package_metadata(self, id): mp = context.get('recorder').current_mediapackage line = mp.metadata_episode duration = mp.getDuration() line["duration"] = long(duration / 1000) if duration else None # FIXME Does series_title need sanitising as well as duration? created = mp.getDate() # line["created"] = calendar.timegm(created.utctimetuple()) for key, value in mp.metadata_series.iteritems(): line["series_" + key] = value for key, value in line.iteritems(): if value in [None, []]: line[key] = '' # return line return line def subscription_callback(self, error): if error: logger.warn("Subscription callback returned error: %s" % error) def insert_callback(self, error, data): if error: logger.warn("Insert callback returned error: %s" % error) def update_callback(self, error, data): if error: logger.warn("Update callback returned error: %s" % error) def on_subscribed(self, subscription): if (subscription == 'GalicasterControl'): me = self.client.find_one('rooms') # Data to push when inserting or updating data = { 'displayName': self.displayName, 'ip': self.ip, 'paused': self.paused, 'recording': self.recording, 'heartbeat': int(time.time()), 'camAvailable': self.cam_available, 'inputs': self.inputs(), 'stream': { 'host': self._stream_host, 'port': self._audiostream_port, 'key': self.stream_key } } # Parse extra Meteor Mongodb collection elements and append for params in self.extra_params_list: param = params.split(':') data[param[0]] = param[1] if self.currentMediaPackage: data['currentMediaPackage'] = self.currentMediaPackage if self.currentProfile: data['currentProfile'] = self.currentProfile if me: # Items to unset unset = {} if not self.currentMediaPackage: unset['currentMediaPackage'] = '' if not self.currentProfile: unset['currentProfile'] = '' # Update to push update = {'$set': data} if unset: update['$unset'] = unset self.update('rooms', {'_id': self.id}, update) else: data['_id'] = self.id self.insert('rooms', data) def inputs(self): inputs = {'presentations': ['Presentation']} inputs['cameras'] = [] labels = conf.get('ddp', 'cam_labels') cam_labels = [] if labels: cam_labels = [l.strip() for l in labels.split(',')] for i in range(0, self.cam_available): label = cam_labels[i] if i < len(cam_labels) else "Camera %d" % ( i + 1) inputs['cameras'].append(label) return inputs def on_added(self, collection, id, fields): pass def on_changed(self, collection, id, fields, cleared): me = self.client.find_one('rooms') if self.paused != me['paused']: self.set_paused(me['paused']) if context.get('recorder').is_recording() != me['recording']: self.set_recording(me) def on_removed(self, collection, id): self.on_subscribed(None) def set_paused(self, new_status): if not self.paused: self.paused = new_status context.get('recorder').pause() else: self.paused = False context.get('recorder').resume() def set_recording(self, me): self.recording = me['recording'] if self.recording: # FIXME: Metadata isn't passed to recorder meta = me.get('currentMediaPackage', {}) or {} profile = me.get('currentProfile', 'nocam') series = (meta.get('series_title', ''), meta.get('isPartOf', '')) user = { 'user_name': meta.get('creator', ''), 'user_id': meta.get('rightsHolder', '') } title = meta.get('title', 'Unknown') context.get('recorder').record() else: context.get('recorder').stop() def on_connected(self): logger.info('Connected to Meteor') token = conf.get('ddp', 'token') self.client.login(self._user, self._password, token=token) def on_logged_in(self, data): conf.set('ddp', 'token', data['token']) conf.update() try: self.client.subscribe('GalicasterControl', params=[self.id], callback=self.subscription_callback) except Exception: logger.warn('DDP subscription failed') def on_closed(self, code, reason): self.has_disconnected = True logger.error('Disconnected from Meteor: err %d - %s' % (code, reason)) def subscribedTo(self, publication): return self.client.subscriptions.get(publication) != None
import time from MeteorClient import MeteorClient client = MeteorClient('ws://127.0.0.1:3000/websocket', debug=True) client.connect() client.subscribe('SessionsList') name = time.strftime("%I:%M:%S") time.sleep(1) client.insert('Sessions', {'title': name}) time.sleep(1) # confirm that a publication was started based on the session name passed in client.subscribe(name) time.sleep(1)
class DDP(Thread): def __init__(self): Thread.__init__(self) self.meteor = conf.get('ddp', 'meteor') self.client = MeteorClient(self.meteor, debug=False) self.client.on('added', self.on_added) self.client.on('changed', self.on_changed) self.client.on('subscribed', self.on_subscribed) self.client.on('connected', self.on_connected) self.client.on('removed', self.on_removed) self.client.on('closed', self.on_closed) self.client.on('logged_in', self.on_logged_in) self.displayName = conf.get('sussexlogin', 'room_name') self.vu_min = -70 self.vu_range = 40 self.do_vu = 0 self.last_vu = None self.ip = socket.gethostbyname(socket.gethostname()) self.id = conf.get('ingest', 'hostname') self._user = conf.get('ddp', 'user') self._password = conf.get('ddp', 'password') self._http_host = conf.get('ddp', 'http_host') self._audiostream_port = conf.get('audiostream', 'port') or 31337 self.netreg_id = conf.get('ddp', 'netreg_id') self.store_audio = conf.get_boolean('ddp', 'store_audio') self.paused = False self.recording = False self.currentMediaPackage = None self.currentProfile = None self.has_disconnected = False cam_available = conf.get( 'sussexlogin', 'cam_available') or cam_available if cam_available in ('True', 'true', True, '1', 1): self.cam_available = 1 elif cam_available in ('False', 'false', False, '0', 0): self.cam_available = 0 else: self.cam_available = int(cam_available) self.audiofaders = [] faders = conf.get('ddp', 'audiofaders').split() for fader in faders: audiofader = {} fader = 'audiofader-' + fader audiofader['name'] = conf.get(fader, 'name') audiofader['display'] = conf.get(fader, 'display') audiofader['min'] = conf.get_int(fader, 'min') audiofader['max'] = conf.get_int(fader, 'max') audiofader['type'] = conf.get(fader, 'type') audiofader['setrec'] = conf.get_boolean(fader, 'setrec') audiofader['mute'] = conf.get_boolean(fader, 'mute') audiofader['unmute'] = conf.get_boolean(fader, 'unmute') audiofader['setlevel'] = conf.get_int(fader, 'setlevel') try: audiofader['control'] = alsaaudio.Mixer( control=audiofader['name']) self.audiofaders.append(audiofader) except Exception as e: logger.warn(e) fd, eventmask = self.audiofaders[0]['control'].polldescriptors()[0] self.watchid = gobject.io_add_watch(fd, eventmask, self.mixer_changed) dispatcher.connect('galicaster-init', self.on_init) dispatcher.connect('update-rec-vumeter', self.vumeter) dispatcher.connect('galicaster-notify-timer-short', self.heartbeat) dispatcher.connect('start-before', self.on_start_recording) dispatcher.connect('restart-preview', self.on_stop_recording) dispatcher.connect('update-rec-status', self.on_rec_status_update) def run(self): self.connect() def connect(self): if not self.has_disconnected: try: self.client.connect() except Exception: logger.warn('DDP connection failed') def update(self, collection, query, update): if self.client.connected and self.subscribedTo('GalicasterControl'): try: self.client.update( collection, query, update, callback=self.update_callback) except Exception: logger.warn( "Error updating document " "{collection: %s, query: %s, update: %s}" % (collection, query, update)) def insert(self, collection, document): if self.client.connected and self.subscribedTo('GalicasterControl'): try: self.client.insert( collection, document, callback=self.insert_callback) except Exception: logger.warn( "Error inserting document {collection: %s, document: %s}" % (collection, document)) def heartbeat(self, element): if self.client.connected: self.update_images() else: self.connect() def on_start_recording(self, sender, id): self.recording = True self.currentMediaPackage = self.media_package_metadata(id) self.currentProfile = context.get_state().profile.name self.update( 'rooms', { '_id': self.id }, { '$set': { 'currentMediaPackage': self.currentMediaPackage, 'currentProfile': self.currentProfile, 'recording': self.recording } }) def on_stop_recording(self, sender=None): self.recording = False self.currentMediaPackage = None self.currentProfile = None self.update( 'rooms', { '_id': self.id }, { '$unset': { 'currentMediaPackage': '', 'currentProfile': '' }, '$set': { 'recording': self.recording } }) self.update_images(1.5) def on_init(self, data): self.update_images(1.5) def update_images(self, delay=0): worker = Thread(target=self._update_images, args=(delay,)) worker.start() def _update_images(self, delay): time.sleep(delay) files = {} audio_devices = ['audiotest', 'autoaudio', 'pulse'] for track in context.get_state().profile.tracks: if track.device not in audio_devices: file = os.path.join('/tmp', track.file + '.jpg') try: if(os.path.getctime(file) > time.time() - 3): files[track.flavor] = (track.flavor + '.jpg', open(file, 'rb'), 'image/jpeg') except Exception: logger.warn("Unable to check date of or open file (%s)" % file) im = ImageGrab.grab(bbox=(10, 10, 1280, 720), backend='imagemagick') im.thumbnail((640, 360)) output = cStringIO.StringIO() if im.mode != "RGB": im = im.convert("RGB") im.save(output, format="JPEG") files['galicaster'] = ('galicaster.jpg', output.getvalue(), 'image/jpeg') try: # add verify=False for testing self signed certs requests.post( "%s/image/%s" % (self._http_host, self.id), files=files, auth=( self._user, self._password)) except Exception: logger.warn('Unable to post images') def mixer_changed(self, source=None, condition=None, reopen=True): if reopen: for audiofader in self.audiofaders: audiofader['control'] = alsaaudio.Mixer( control=audiofader['name']) self.update_audio() return True def vumeter(self, element, data): if self.do_vu == 0: if data == "Inf": data = 0 else: if data < -self.vu_range: data = -self.vu_range elif data > 0: data = 0 data = int(((data + self.vu_range) / float(self.vu_range)) * 100) if data != self.last_vu: update = {'vumeter': data} self.update('rooms', {'_id': self.id}, {'$set': update}) self.last_vu = data self.do_vu = (self.do_vu + 1) % 20 def on_rec_status_update(self, element, data): is_paused = data == 'Paused' if is_paused: self.update_images(.75) if self.paused != is_paused: self.update( 'rooms', { '_id': self.id}, { '$set': { 'paused': is_paused}}) self.paused = is_paused if data == ' Recording ': subprocess.call(['killall', 'maliit-server']) self.update_images(.75) def media_package_metadata(self, id): mp = context.get_repository().get(id) line = mp.metadata_episode.copy() duration = mp.getDuration() line["duration"] = long(duration / 1000) if duration else None # Does series_title need sanitising as well as duration? created = mp.getDate() line["created"] = calendar.timegm(created.utctimetuple()) for key, value in mp.metadata_series.iteritems(): line["series_" + key] = value for key, value in line.iteritems(): if value in [None, []]: line[key] = '' return line def subscription_callback(self, error): if error: logger.warn("Subscription callback returned error: %s" % error) def insert_callback(self, error, data): if error: logger.warn("Insert callback returned error: %s" % error) def update_callback(self, error, data): if error: logger.warn("Update callback returned error: %s" % error) def on_subscribed(self, subscription): if(subscription == 'GalicasterControl'): me = self.client.find_one('rooms') stream_key = uuid.uuid4().get_hex() # Data to push when inserting or updating data = { 'displayName': self.displayName, 'ip': self.ip, 'paused': self.paused, 'recording': self.recording, 'heartbeat': int(time.time()), 'camAvailable': self.cam_available, 'netregId': self.netreg_id, 'inputs': self.inputs(), 'stream': { 'port': self._audiostream_port, 'key': stream_key }, 'galicasterVersion': galicaster.__version__ } if self.currentMediaPackage: data['currentMediaPackage'] = self.currentMediaPackage if self.currentProfile: data['currentProfile'] = self.currentProfile if me: # Items to unset unset = {} if not self.currentMediaPackage: unset['currentMediaPackage'] = '' if not self.currentProfile: unset['currentProfile'] = '' # Update to push update = { '$set': data } if unset: update['$unset'] = unset self.update('rooms', {'_id': self.id}, update) else: audio = self.read_audio_settings() data['_id'] = self.id data['audio'] = audio self.insert('rooms', data) def inputs(self): inputs = { 'presentations': ['Presentation'] } inputs['cameras'] = [] labels = conf.get('sussexlogin', 'matrix_cam_labels') cam_labels = [] if labels: cam_labels = [l.strip() for l in labels.split(',')] for i in range(0, self.cam_available): label = cam_labels[i] if i < len( cam_labels) else "Camera %d" % (i + 1) inputs['cameras'].append(label) return inputs def set_audio(self, fields): faders = fields.get('audio') if faders: for fader in faders: mixer = None level = fader.get('level') for audiofader in self.audiofaders: if audiofader['name'] == fader['name']: mixer = audiofader['control'] break if mixer: l, r = mixer.getvolume(fader['type']) if level >= 0 and l != level: mixer.setvolume(level, 0, fader['type']) mixer.setvolume(level, 1, fader['type']) if self.store_audio: # Relies on no password sudo access for current user to alsactl subprocess.call(['sudo', 'alsactl', 'store']) def on_added(self, collection, id, fields): self.set_audio(fields) self.update_audio() def on_changed(self, collection, id, fields, cleared): self.set_audio(fields) me = self.client.find_one('rooms') if self.paused != me['paused']: self.set_paused(me['paused']) if context.get_state().is_recording != me['recording']: self.set_recording(me) def on_removed(self, collection, id): self.on_subscribed(None) def set_paused(self, new_status): self.paused = new_status dispatcher.emit("toggle-pause-rec") def set_recording(self, me): self.recording = me['recording'] if self.recording: meta = me.get('currentMediaPackage', {}) or {} profile = me.get('currentProfile', 'nocam') series = (meta.get('series_title', ''), meta.get('isPartOf', '')) user = {'user_name': meta.get('creator', ''), 'user_id': meta.get('rightsHolder', '')} title = meta.get('title', 'Unknown') dispatcher.emit('sussexlogin-record', (user, title, series, profile)) else: dispatcher.emit("stop-record", '') def on_connected(self): logger.info('Connected to Meteor') token = conf.get('ddp', 'token') self.client.login(self._user, self._password, token=token) def on_logged_in(self, data): conf.set('ddp', 'token', data['token']) conf.update() try: self.client.subscribe( 'GalicasterControl', params=[ self.id], callback=self.subscription_callback) except Exception: logger.warn('DDP subscription failed') def on_closed(self, code, reason): self.has_disconnected = True logger.error('Disconnected from Meteor: err %d - %s' % (code, reason)) def update_audio(self): me = self.client.find_one('rooms') audio = self.read_audio_settings() update = False if me: mAudio = me.get('audio') mAudioNames = [x['name'] for x in mAudio] audioNames = [x['name'] for x in audio] if set(mAudioNames) != set(audioNames): update = True if not update: for key, fader in enumerate(audio): if mAudio[key].get('level') != fader.get('level'): update = True if update: self.update( 'rooms', { '_id': self.id}, { '$set': { 'audio': audio}}) def read_audio_settings(self): audio_settings = [] for audiofader in self.audiofaders: if audiofader['display']: audio_settings.append( self.control_values(audiofader) ) # ensure fixed values mixer = audiofader['control'] if audiofader['setrec']: mixer.setrec(1) if audiofader['mute']: mixer.setmute(1) if audiofader['unmute']: mixer.setmute(0) if audiofader['setlevel'] >= 0: mixer.setvolume(audiofader['setlevel'], 0, audiofader['type']) if 'Joined Playback Volume' not in mixer.volumecap(): mixer.setvolume(audiofader['setlevel'], 1, audiofader['type']) return audio_settings def control_values(self, audiofader): controls = {} left, right = audiofader['control'].getvolume(audiofader['type']) controls['min'] = audiofader['min'] controls['max'] = audiofader['max'] controls['level'] = left controls['type'] = audiofader['type'] controls['name'] = audiofader['name'] controls['display'] = audiofader['display'] return controls def subscribedTo(self, publication): return self.client.subscriptions.get(publication) is not None
class DDP(Thread): def __init__(self): Thread.__init__(self) self.meteor = conf.get('ddp', 'meteor') self.client = MeteorClient(self.meteor, debug=False) self.client.on('added', self.on_added) self.client.on('changed', self.on_changed) self.client.on('subscribed', self.on_subscribed) self.client.on('connected', self.on_connected) self.client.on('removed', self.on_removed) self.client.on('closed', self.on_closed) self.client.on('logged_in', self.on_logged_in) self.displayName = conf.get('sussexlogin', 'room_name') self.vu_min = -70 self.vu_range = 40 self.do_vu = 0 self.last_vu = None self.ip = socket.gethostbyname(socket.gethostname()) self.id = conf.get('ingest', 'hostname') self._user = conf.get('ddp', 'user') self._password = conf.get('ddp', 'password') self._http_host = conf.get('ddp', 'http_host') self._audiostream_port = conf.get('audiostream', 'port') or 31337 self.netreg_id = conf.get('ddp', 'netreg_id') self.store_audio = conf.get_boolean('ddp', 'store_audio') self.paused = False self.recording = False self.currentMediaPackage = None self.currentProfile = None self.has_disconnected = False cam_available = conf.get('sussexlogin', 'cam_available') or cam_available if cam_available in ('True', 'true', True, '1', 1): self.cam_available = 1 elif cam_available in ('False', 'false', False, '0', 0): self.cam_available = 0 else: self.cam_available = int(cam_available) self.audiofaders = [] faders = conf.get('ddp', 'audiofaders').split() for fader in faders: audiofader = {} fader = 'audiofader-' + fader audiofader['name'] = conf.get(fader, 'name') audiofader['display'] = conf.get(fader, 'display') audiofader['min'] = conf.get_int(fader, 'min') audiofader['max'] = conf.get_int(fader, 'max') audiofader['type'] = conf.get(fader, 'type') audiofader['setrec'] = conf.get_boolean(fader, 'setrec') audiofader['mute'] = conf.get_boolean(fader, 'mute') audiofader['unmute'] = conf.get_boolean(fader, 'unmute') audiofader['setlevel'] = conf.get_int(fader, 'setlevel') try: audiofader['control'] = alsaaudio.Mixer( control=audiofader['name']) self.audiofaders.append(audiofader) except Exception as e: logger.warn(e) fd, eventmask = self.audiofaders[0]['control'].polldescriptors()[0] self.watchid = gobject.io_add_watch(fd, eventmask, self.mixer_changed) dispatcher.connect('galicaster-init', self.on_init) dispatcher.connect('update-rec-vumeter', self.vumeter) dispatcher.connect('galicaster-notify-timer-short', self.heartbeat) dispatcher.connect('start-before', self.on_start_recording) dispatcher.connect('restart-preview', self.on_stop_recording) dispatcher.connect('update-rec-status', self.on_rec_status_update) def run(self): self.connect() def connect(self): if not self.has_disconnected: try: self.client.connect() except Exception: logger.warn('DDP connection failed') def update(self, collection, query, update): if self.client.connected and self.subscribedTo('GalicasterControl'): try: self.client.update(collection, query, update, callback=self.update_callback) except Exception: logger.warn("Error updating document " "{collection: %s, query: %s, update: %s}" % (collection, query, update)) def insert(self, collection, document): if self.client.connected and self.subscribedTo('GalicasterControl'): try: self.client.insert(collection, document, callback=self.insert_callback) except Exception: logger.warn( "Error inserting document {collection: %s, document: %s}" % (collection, document)) def heartbeat(self, element): if self.client.connected: self.update_images() else: self.connect() def on_start_recording(self, sender, id): self.recording = True self.currentMediaPackage = self.media_package_metadata(id) self.currentProfile = context.get_state().profile.name self.update('rooms', {'_id': self.id}, { '$set': { 'currentMediaPackage': self.currentMediaPackage, 'currentProfile': self.currentProfile, 'recording': self.recording } }) def on_stop_recording(self, sender=None): self.recording = False self.currentMediaPackage = None self.currentProfile = None self.update('rooms', {'_id': self.id}, { '$unset': { 'currentMediaPackage': '', 'currentProfile': '' }, '$set': { 'recording': self.recording } }) self.update_images(1.5) def on_init(self, data): self.update_images(1.5) def update_images(self, delay=0): worker = Thread(target=self._update_images, args=(delay, )) worker.start() def _update_images(self, delay): time.sleep(delay) files = {} audio_devices = ['audiotest', 'autoaudio', 'pulse'] for track in context.get_state().profile.tracks: if track.device not in audio_devices: file = os.path.join('/tmp', track.file + '.jpg') try: if (os.path.getctime(file) > time.time() - 3): files[track.flavor] = (track.flavor + '.jpg', open(file, 'rb'), 'image/jpeg') except Exception: logger.warn("Unable to check date of or open file (%s)" % file) im = ImageGrab.grab(bbox=(10, 10, 1280, 720), backend='imagemagick') im.thumbnail((640, 360)) output = cStringIO.StringIO() if im.mode != "RGB": im = im.convert("RGB") im.save(output, format="JPEG") files['galicaster'] = ('galicaster.jpg', output.getvalue(), 'image/jpeg') try: # add verify=False for testing self signed certs requests.post("%s/image/%s" % (self._http_host, self.id), files=files, auth=(self._user, self._password)) except Exception: logger.warn('Unable to post images') def mixer_changed(self, source=None, condition=None, reopen=True): if reopen: for audiofader in self.audiofaders: audiofader['control'] = alsaaudio.Mixer( control=audiofader['name']) self.update_audio() return True def vumeter(self, element, data): if self.do_vu == 0: if data == "Inf": data = 0 else: if data < -self.vu_range: data = -self.vu_range elif data > 0: data = 0 data = int(((data + self.vu_range) / float(self.vu_range)) * 100) if data != self.last_vu: update = {'vumeter': data} self.update('rooms', {'_id': self.id}, {'$set': update}) self.last_vu = data self.do_vu = (self.do_vu + 1) % 20 def on_rec_status_update(self, element, data): is_paused = data == 'Paused' if is_paused: self.update_images(.75) if self.paused != is_paused: self.update('rooms', {'_id': self.id}, {'$set': { 'paused': is_paused }}) self.paused = is_paused if data == ' Recording ': subprocess.call(['killall', 'maliit-server']) self.update_images(.75) def media_package_metadata(self, id): mp = context.get_repository().get(id) line = mp.metadata_episode.copy() duration = mp.getDuration() line["duration"] = long(duration / 1000) if duration else None # Does series_title need sanitising as well as duration? created = mp.getDate() line["created"] = calendar.timegm(created.utctimetuple()) for key, value in mp.metadata_series.iteritems(): line["series_" + key] = value for key, value in line.iteritems(): if value in [None, []]: line[key] = '' return line def subscription_callback(self, error): if error: logger.warn("Subscription callback returned error: %s" % error) def insert_callback(self, error, data): if error: logger.warn("Insert callback returned error: %s" % error) def update_callback(self, error, data): if error: logger.warn("Update callback returned error: %s" % error) def on_subscribed(self, subscription): if (subscription == 'GalicasterControl'): me = self.client.find_one('rooms') stream_key = uuid.uuid4().get_hex() # Data to push when inserting or updating data = { 'displayName': self.displayName, 'ip': self.ip, 'paused': self.paused, 'recording': self.recording, 'heartbeat': int(time.time()), 'camAvailable': self.cam_available, 'netregId': self.netreg_id, 'inputs': self.inputs(), 'stream': { 'port': self._audiostream_port, 'key': stream_key }, 'galicasterVersion': galicaster.__version__ } if self.currentMediaPackage: data['currentMediaPackage'] = self.currentMediaPackage if self.currentProfile: data['currentProfile'] = self.currentProfile if me: # Items to unset unset = {} if not self.currentMediaPackage: unset['currentMediaPackage'] = '' if not self.currentProfile: unset['currentProfile'] = '' # Update to push update = {'$set': data} if unset: update['$unset'] = unset self.update('rooms', {'_id': self.id}, update) else: audio = self.read_audio_settings() data['_id'] = self.id data['audio'] = audio self.insert('rooms', data) def inputs(self): inputs = {'presentations': ['Presentation']} inputs['cameras'] = [] labels = conf.get('sussexlogin', 'matrix_cam_labels') cam_labels = [] if labels: cam_labels = [l.strip() for l in labels.split(',')] for i in range(0, self.cam_available): label = cam_labels[i] if i < len(cam_labels) else "Camera %d" % ( i + 1) inputs['cameras'].append(label) return inputs def set_audio(self, fields): faders = fields.get('audio') if faders: for fader in faders: mixer = None level = fader.get('level') for audiofader in self.audiofaders: if audiofader['name'] == fader['name']: mixer = audiofader['control'] break if mixer: l, r = mixer.getvolume(fader['type']) if level >= 0 and l != level: mixer.setvolume(level, 0, fader['type']) mixer.setvolume(level, 1, fader['type']) if self.store_audio: # Relies on no password sudo access for current user to alsactl subprocess.call(['sudo', 'alsactl', 'store']) def on_added(self, collection, id, fields): self.set_audio(fields) self.update_audio() def on_changed(self, collection, id, fields, cleared): self.set_audio(fields) me = self.client.find_one('rooms') if self.paused != me['paused']: self.set_paused(me['paused']) if context.get_state().is_recording != me['recording']: self.set_recording(me) def on_removed(self, collection, id): self.on_subscribed(None) def set_paused(self, new_status): self.paused = new_status dispatcher.emit("toggle-pause-rec") def set_recording(self, me): self.recording = me['recording'] if self.recording: meta = me.get('currentMediaPackage', {}) or {} profile = me.get('currentProfile', 'nocam') series = (meta.get('series_title', ''), meta.get('isPartOf', '')) user = { 'user_name': meta.get('creator', ''), 'user_id': meta.get('rightsHolder', '') } title = meta.get('title', 'Unknown') dispatcher.emit('sussexlogin-record', (user, title, series, profile)) else: dispatcher.emit("stop-record", '') def on_connected(self): logger.info('Connected to Meteor') token = conf.get('ddp', 'token') self.client.login(self._user, self._password, token=token) def on_logged_in(self, data): conf.set('ddp', 'token', data['token']) conf.update() try: self.client.subscribe('GalicasterControl', params=[self.id], callback=self.subscription_callback) except Exception: logger.warn('DDP subscription failed') def on_closed(self, code, reason): self.has_disconnected = True logger.error('Disconnected from Meteor: err %d - %s' % (code, reason)) def update_audio(self): me = self.client.find_one('rooms') audio = self.read_audio_settings() update = False if me: mAudio = me.get('audio') mAudioNames = [x['name'] for x in mAudio] audioNames = [x['name'] for x in audio] if set(mAudioNames) != set(audioNames): update = True if not update: for key, fader in enumerate(audio): if mAudio[key].get('level') != fader.get('level'): update = True if update: self.update('rooms', {'_id': self.id}, {'$set': { 'audio': audio }}) def read_audio_settings(self): audio_settings = [] for audiofader in self.audiofaders: if audiofader['display']: audio_settings.append(self.control_values(audiofader)) # ensure fixed values mixer = audiofader['control'] if audiofader['setrec']: mixer.setrec(1) if audiofader['mute']: mixer.setmute(1) if audiofader['unmute']: mixer.setmute(0) if audiofader['setlevel'] >= 0: mixer.setvolume(audiofader['setlevel'], 0, audiofader['type']) if 'Joined Playback Volume' not in mixer.volumecap(): mixer.setvolume(audiofader['setlevel'], 1, audiofader['type']) return audio_settings def control_values(self, audiofader): controls = {} left, right = audiofader['control'].getvolume(audiofader['type']) controls['min'] = audiofader['min'] controls['max'] = audiofader['max'] controls['level'] = left controls['type'] = audiofader['type'] controls['name'] = audiofader['name'] controls['display'] = audiofader['display'] return controls def subscribedTo(self, publication): return self.client.subscriptions.get(publication) is not None
class DDP(Thread): def __init__(self): Thread.__init__(self) self.meteor = conf.get('ddp', 'meteor') self.client = MeteorClient(self.meteor, debug=False) self.client.on('added', self.on_added) self.client.on('changed', self.on_changed) self.client.on('subscribed', self.on_subscribed) self.client.on('connected', self.on_connected) self.client.on('removed', self.on_removed) self.client.on('closed', self.on_closed) self.client.on('logged_in', self.on_logged_in) self.displayName = conf.get('ddp', 'room_name') self.vu_min = -50 self.vu_range = 50 self.vu_data = 0 self.last_vu = None self.ip = conf.get('ingest', 'address') self.id = conf.get('ingest', 'hostname') self._user = conf.get('ddp', 'user') self._password = conf.get('ddp', 'password') self._http_host = conf.get('ddp', 'http_host') self._audiostream_port = conf.get('audiostream', 'port') or 31337 self.store_audio = conf.get_boolean('ddp', 'store_audio') self.screenshot_file = conf.get('ddp', 'existing_screenshot') self.high_quality = conf.get_boolean('ddp', 'hq_snapshot') self.paused = False self.recording = False self.currentMediaPackage = None self.currentProfile = None self.has_disconnected = False screen = Gdk.Screen.get_default() self._screen_width = screen.get_width() self._screen_height = screen.get_height() self.cardindex = None cam_available = conf.get( 'ddp', 'cam_available') or 0 if cam_available in ('True', 'true', True, '1', 1): self.cam_available = 1 elif cam_available in ('False', 'false', False, '0', 0): self.cam_available = 0 else: self.cam_available = int(cam_available) # Getting audiostream params. either using existing audiostreaming server like icecast or the audiostream plugin if conf.get('ddp', 'existing_stream_host'): self._stream_host = conf.get('ddp', 'existing_stream_host') else: self._stream_host = self.ip if conf.get_int('ddp', 'existing_stream_port'): self._audiostream_port = conf.get_int('ddp', 'existing_stream_port') else: self._audiostream_port = conf.get_int('audiostream', 'port') or 31337 if conf.get('ddp', 'existing_stream_key'): self.stream_key = conf.get('ddp', 'existing_stream_key') else: self.stream_key = uuid.uuid4().get_hex() if conf.get('ddp', 'extra_params'): self.extra_params_list = conf.get('ddp', 'extra_params').split(';') else: self.extra_params_list = [] logger.info('audiostream URI: {}'.format('http://' + self._stream_host + ':' + str(self._audiostream_port) + '/' + self.stream_key)) dispatcher.connect('init', self.on_init) dispatcher.connect('recorder-vumeter', self.vumeter) dispatcher.connect('timer-short', self.update_vu) dispatcher.connect('timer-short', self.heartbeat) dispatcher.connect('recorder-started', self.on_start_recording) dispatcher.connect('recorder-stopped', self.on_stop_recording) dispatcher.connect('recorder-status', self.on_rec_status_update) def run(self): self.connect() def connect(self): if not self.has_disconnected: try: self.client.connect() except Exception: logger.warn('DDP connection failed') def update(self, collection, query, update): if self.client.connected and self.subscribedTo('GalicasterControl'): try: self.client.update( collection, query, update, callback=self.update_callback) except Exception: logger.warn( "Error updating document " "{collection: %s, query: %s, update: %s}" % (collection, query, update)) def insert(self, collection, document): if self.client.connected and self.subscribedTo('GalicasterControl'): try: self.client.insert( collection, document, callback=self.insert_callback) except Exception: logger.warn( "Error inserting document {collection: %s, document: %s}" % (collection, document)) def heartbeat(self, element): if self.client.connected: self.update_images() else: self.connect() def on_start_recording(self, sender, id): self.recording = True self.currentMediaPackage = self.media_package_metadata(id) self.currentProfile = conf.get_current_profile().name self.update( 'rooms', { '_id': self.id }, { '$set': { 'currentMediaPackage': self.currentMediaPackage, 'currentProfile': self.currentProfile, 'recording': self.recording } }) def on_stop_recording(self, mpid, sender=None): self.recording = False self.currentMediaPackage = None self.currentProfile = None self.update( 'rooms', { '_id': self.id }, { '$unset': { 'currentMediaPackage': '', 'currentProfile': '' }, '$set': { 'recording': self.recording } }) self.update_images(1.5) def on_init(self, data): self.update_images(1.5) def update_images(self, delay=0.0): worker = Thread(target=self._update_images, args=(delay,)) worker.start() def _update_images(self, delay): time.sleep(delay) files = {} if not self.screenshot_file: # take a screenshot with pyscreenshot im = ImageGrab.grab(bbox=(0, 0, self._screen_width, self._screen_height), backend='imagemagick') else: try: # used if screenshot already exists im = Image.open(self.screenshot_file) except IOError as e: logger.warn("Unable to open screenshot file {0}".format(self.screenshot_file)) return output = cStringIO.StringIO() image_format = 'JPEG' if not self.high_quality: im.thumbnail((640, 360), Image.ANTIALIAS) else: image_format = 'PNG' if im.mode != "RGB": im = im.convert("RGB") im.save(output, format=image_format) # to reduce jpeg size use param: optimize=True files['galicaster'] = ('galicaster.jpg', output.getvalue(), 'image/jpeg') try: # add verify=False for testing self signed certs requests.post( "%s/image/%s" % (self._http_host, self.id), files=files, auth=( self._user, self._password)) # to ignore ssl verification, use param: verify=False except Exception: logger.warn('Unable to post images') def vumeter(self, element, data, data_chan2, vu_bool): if data == "Inf": data = 0 else: if data < -self.vu_range: data = -self.vu_range elif data > 0: data = 0 self.vu_data = int(((data + self.vu_range) / float(self.vu_range)) * 100) def update_vu(self, element): if self.vu_data != self.last_vu: update = {'vumeter': self.vu_data} self.update('rooms', {'_id': self.id}, {'$set': update}) self.last_vu = self.vu_data def on_rec_status_update(self, element, data): if data == 'paused': is_paused = True else: is_paused = False if is_paused: self.update_images(.75) if self.paused == is_paused: self.update( 'rooms', { '_id': self.id}, { '$set': { 'paused': is_paused}}) self.paused = is_paused if data == 'recording': self.update_images(.75) def media_package_metadata(self, id): mp = context.get('recorder').current_mediapackage line = mp.metadata_episode duration = mp.getDuration() line["duration"] = long(duration / 1000) if duration else None # FIXME Does series_title need sanitising as well as duration? created = mp.getDate() # line["created"] = calendar.timegm(created.utctimetuple()) for key, value in mp.metadata_series.iteritems(): line["series_" + key] = value for key, value in line.iteritems(): if value in [None, []]: line[key] = '' # return line return line def subscription_callback(self, error): if error: logger.warn("Subscription callback returned error: %s" % error) def insert_callback(self, error, data): if error: logger.warn("Insert callback returned error: %s" % error) def update_callback(self, error, data): if error: logger.warn("Update callback returned error: %s" % error) def on_subscribed(self, subscription): if(subscription == 'GalicasterControl'): me = self.client.find_one('rooms') # Data to push when inserting or updating data = { 'displayName': self.displayName, 'ip': self.ip, 'paused': self.paused, 'recording': self.recording, 'heartbeat': int(time.time()), 'camAvailable': self.cam_available, 'inputs': self.inputs(), 'stream': { 'host': self._stream_host, 'port': self._audiostream_port, 'key': self.stream_key } } # Parse extra Meteor Mongodb collection elements and append for params in self.extra_params_list: param = params.split(':') data[param[0]] = param[1] if self.currentMediaPackage: data['currentMediaPackage'] = self.currentMediaPackage if self.currentProfile: data['currentProfile'] = self.currentProfile if me: # Items to unset unset = {} if not self.currentMediaPackage: unset['currentMediaPackage'] = '' if not self.currentProfile: unset['currentProfile'] = '' # Update to push update = { '$set': data } if unset: update['$unset'] = unset self.update('rooms', {'_id': self.id}, update) else: data['_id'] = self.id self.insert('rooms', data) def inputs(self): inputs = { 'presentations': ['Presentation'] } inputs['cameras'] = [] labels = conf.get('ddp', 'cam_labels') cam_labels = [] if labels: cam_labels = [l.strip() for l in labels.split(',')] for i in range(0, self.cam_available): label = cam_labels[i] if i < len( cam_labels) else "Camera %d" % (i + 1) inputs['cameras'].append(label) return inputs def on_added(self, collection, id, fields): pass def on_changed(self, collection, id, fields, cleared): me = self.client.find_one('rooms') if self.paused != me['paused']: self.set_paused(me['paused']) if context.get('recorder').is_recording() != me['recording']: self.set_recording(me) def on_removed(self, collection, id): self.on_subscribed(None) def set_paused(self, new_status): if not self.paused: self.paused = new_status context.get('recorder').pause() else: self.paused = False context.get('recorder').resume() def set_recording(self, me): self.recording = me['recording'] if self.recording: # FIXME: Metadata isn't passed to recorder meta = me.get('currentMediaPackage', {}) or {} profile = me.get('currentProfile', 'nocam') series = (meta.get('series_title', ''), meta.get('isPartOf', '')) user = {'user_name': meta.get('creator', ''), 'user_id': meta.get('rightsHolder', '')} title = meta.get('title', 'Unknown') context.get('recorder').record() else: context.get('recorder').stop() def on_connected(self): logger.info('Connected to Meteor') token = conf.get('ddp', 'token') self.client.login(self._user, self._password, token=token) def on_logged_in(self, data): conf.set('ddp', 'token', data['token']) conf.update() try: self.client.subscribe( 'GalicasterControl', params=[ self.id], callback=self.subscription_callback) except Exception: logger.warn('DDP subscription failed') def on_closed(self, code, reason): self.has_disconnected = True logger.error('Disconnected from Meteor: err %d - %s' % (code, reason)) def subscribedTo(self, publication): return self.client.subscriptions.get(publication) != None
def processDirectory(path, newCorporaName, newCorporaID, nextDocumentID): print("Processing files...") # Connect to Pterraformer client = MeteorClient('ws://127.0.0.1:4567/websocket') client.connect() # ID of the next available Document record id = nextDocumentID # ID of the next available Corpus record corpusID = newCorporaID # Create a new Collection client.insert('corpora', { '_id': str(corpusID), 'owner': "public", 'properties': { '@context': 'http://schema.org', 'name': newCorporaName, 'modifyDate': '14/6/14'}}) types = ('./*.pdf', './*.docx', './*.txt') # the tuple of file types # Must work with the path object to allow specification of the correct directory files_grabbed = [] for files in types: files_grabbed.extend(glob.glob(files)) files_grabbed # the list of pdf and cpp files for filename in files_grabbed: print(filename) fileExtensionType = filename.split(".")[-1] if (fileExtensionType == "pdf"): rawText = convert_pdf_to_txt(filename) elif (fileExtensionType == "docx"): rawText = convert_docx_to_txt(filename) else: rawText = convert_txt_to_txt(filename) id2 = id id = id + 1 #print(rawText[:50]) client.insert('documents', { '_id': str(id2), 'corpus': str(corpusID), 'rawText': rawText, 'properties': { '@context': 'http://schema.org/', '@type': 'CreativeWork', 'name': filename, 'modifyDate': '13/4/2015' }, 'parsingResults': { }, 'placeReferences': [ ], 'matchedPlaces': { "type": "FeatureCollection", "features": [] }, 'markedUpText': "hello" }, callback=insert_callback)
import time from MeteorClient import MeteorClient client = MeteorClient('ws://127.0.0.1:3000/websocket', debug=True) client.connect() client.subscribe('SessionsList') time.sleep(1) sessions = client.find('sessions') session_to_join = sessions[0] client.subscribe(session_to_join['title']) # GPS data should follow this time.sleep(1) for i in range(0, 5): gps = { 'sessionID': session_to_join['_id'], 'lat': 110, 'long': 220, 'time': time.strftime("%I:%M:%S") } client.insert("GPSData", gps) time.sleep(2)