def failover_audio(self, mpUri): flavour = 'presenter/source' mp_list = context.get_repository() for uid,mp in mp_list.iteritems(): if mp.getURI() == mpUri: logger.debug('Found MP') #compare rms from pipeline with set threshold with open(temp_amp) as f: amp_list = f.readlines() f.close() pipeline_amp = float(max(amp_list)) if MAX_AMPLITUDE is None: threshold = default_max_amplitude else: threshold = MAX_AMPLITUDE if pipeline_amp <= float(threshold): # if multiple temp mp3 files, merge if filecount(FAIL_DIR) > 1: merge(FAIL_DIR) logger.info('audio quiet - will be replaced') filename = get_audio_track() dest = os.path.join(mpUri, os.path.basename(filename)) shutil.copyfile(FAILOVER_FILE, dest) mp.add(dest, mediapackage.TYPE_TRACK, flavour, FAILOVER_MIMETYPE, mp.getDuration()) mp_list.update(mp) logger.info('Replaced quite audio with failover recording UID:%s - URI: %s', uid, mpUri) remove_temp(FAIL_DIR, temp_amp) else: remove_temp(FAIL_DIR, temp_amp)
def test_cleanstale_plugin(self): dispatcher = context.get_dispatcher() repo = context.get_repository() conf = context.get_conf() now = datetime.datetime.utcnow() mp = mediapackage.Mediapackage(identifier="1", title='MP#1', date=(now - datetime.timedelta(days=1))) repo.add(mp) mp = mediapackage.Mediapackage(identifier="2", title='MP#2', date=(now - datetime.timedelta(days=30))) repo.add(mp) mp = mediapackage.Mediapackage(identifier="3", title='MP#3', date=(now - datetime.timedelta(days=60))) repo.add(mp) mp = mediapackage.Mediapackage(identifier="4", title='MP#4', date=(now + datetime.timedelta(days=1))) repo.add(mp) mp = mediapackage.Mediapackage(identifier="5", title='MP#5', date=(now + datetime.timedelta(days=30))) repo.add(mp) conf.set('cleanstale','maxarchivaldays', '70') cleanstale.init() self.assertEqual(len(repo), 5) conf.set('cleanstale','maxarchivaldays', '50') cleanstale.init() dispatcher.emit('timer-nightly') self.assertEqual(len(repo), 4) conf.set('cleanstale','maxarchivaldays', '20') cleanstale.init() dispatcher.emit('timer-nightly') self.assertEqual(len(repo), 3)
def list(): repo = context.get_repository() response.content_type = 'application/json' keys = [] for key,value in repo.iteritems(): keys.append(key) return json.dumps(keys)
def get_series(): repo = context.get_repository() mhclient = context.get_mhclient() try: series_json = mhclient.getseries() repo.save_attach('series.json', series_json) except: try: series_json = repo.get_attach('series.json').read() except: series_json = '{"totalCount":"0","catalogs":[]}' series_json = json.loads(series_json) # convert JSON in ARRAY out = {} for series in series_json['catalogs']: k = series['http://purl.org/dc/terms/']['identifier'][0]['value'] group = {} for parameter in series['http://purl.org/dc/terms/'].iterkeys(): try: group[parameter] = series['http://purl.org/dc/terms/'][parameter][0]['value'] except: group[parameter] = None out[k] = group return out
def get_series(): repo = context.get_repository() ocservice = context.get_ocservice() # Import the 'series' section as a dictionary series_conf = context.get_conf().get_section('series') # Init 'queries' dictionary queries = {'startPage': 0, 'count': RESULTS_PER_PAGE} # Filter out keys that do not refer to a certain series property # Also, substitute any placeholder(s) used to filter the series # TODO Currently the only placeholder is {user} for key in series_conf.keys(): if key not in DISALLOWED_QUERIES: try: queries[key] = series_conf[key].format(**MAPPINGS) except KeyError: # If the placeholder does not exist, log the issue but ignore it # TODO Log the exception pass try: series_list = [] check_default = True while True: if not ocservice.net: break series_json = json.loads(ocservice.client.getseries(**queries)) for catalog in series_json['catalogs']: try: series_list.append(parse_json_series(catalog)) except KeyError: # Ignore ill-formated series pass if len(series_list) >= int(series_json['totalCount']): # Check the default series is present, otherwise query for it if 'default' in series_conf and check_default and series_conf['default'] not in dict(series_list): check_default = False queries = { "seriesId": series_conf['default'] } else: break else: queries['startPage'] += 1 repo.save_attach('series.json', json.dumps(series_list)) except (ValueError, IOError, RuntimeError, AttributeError): #TODO Log the exception try: series_list = json.load(repo.get_attach('series.json')) except (ValueError, IOError): #TODO Log the exception series_list = [] return series_list
def __init__(self, element): """elements set the previous area to which the top bar's back button points to""" Gtk.Box.__init__(self) self.strip = StripUI(element) self.conf = context.get_conf() self.dispatcher = context.get_dispatcher() self.repository = context.get_repository() self.network = False self.dispatcher.connect_ui("opencast-status", self.network_status)
def clear_job(sender=None): logger.info("Executing clear job ...") conf = context.get_conf() repo = context.get_repository() mps = repo.get_past_mediapackages(days) logger.info("Found {} MP that have more than {} days". format(len(mps), days)) for mp in mps: logger.info("Removing MP {}".format(mp.getIdentifier())) repo.delete(mp)
def __init__(self, element): """elements set the previous area to which the top bar's back button points to""" gtk.Box.__init__(self) self.strip = StripUI(element) self.conf = context.get_conf() self.dispatcher = context.get_dispatcher() self.repository = context.get_repository() self.network = False self.dispatcher.connect("net-up", self.network_status, True) self.dispatcher.connect("net-down", self.network_status, False)
def metadata(id): response.content_type = 'application/json' mp = context.get_repository().get(id) line = mp.metadata_episode.copy() line["duration"] = long(mp.getDuration()/1000) line["created"] = mp.getStartDateAsString() for key,value in mp.metadata_series.iteritems(): line["series-"+key] = value for key,value in line.iteritems(): if value in [None,[]]: line[key]='' json.dumps({key:value}) return json.dumps(line)
def init(): global conf, logger, repo, dispatcher, minfreespace, freespace conf = context.get_conf() dispatcher = context.get_dispatcher() logger = context.get_logger() repo = context.get_repository() minfreespace = conf.get_int('checkspace','minfreespace') if not minfreespace: raise Exception("Parameter minfreespace not configured") logger.info("Parameter 'minfreespace' set to {} GB".format(minfreespace)) dispatcher.connect("recorder-ready", check_space)
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 clear_job(sender=None): conf = context.get_conf() repo = context.get_repository() try: days = int(conf.get('cleanstale','maxarchivaldays')) except ValueError: #log return mps = repo.get_past_mediapackages(days) for mp in mps: #log repo.delete(mp)
def test_send_to_moniviestin_plugin(self): dispatcher = context.get_dispatcher() repo = context.get_repository() conf = context.get_conf() conf.set("moniviestin", "url", "http://yska.psy.jyu.fi:8080/uliuli") mp = self.create_mediapackage() repo.add(mp) send_to_moniviestin.init() self.assertEqual(len(repo), 1) dispatcher.emit('stop-operation', 'ingest', mp, True) self.assertEqual(len(repo), 1) dispatcher.emit('stop-operation', 'ingest', mp, False) self.assertEqual(len(repo), 1)
def check_repository(self): #mp_list is collection of mediapackages ID's mp_list = context.get_repository() for uid,mp in mp_list.iteritems(): if mp.status == mediapackage.SCHEDULED and mp.getDate() < datetime.datetime.utcnow() and mp.getDate()+datetime.timedelta(seconds=(mp.getDuration()/1000)) > datetime.datetime.utcnow(): #duration update x = datetime.datetime.utcnow() - mp.getDate() x = x.seconds-2 mp.setDuration(mp.getDuration() - x*1000) #start-datetime update mp.setDate(datetime.datetime.utcnow()+datetime.timedelta(seconds=2)) #repository update mp_list.update(mp) scheduler = context.get_scheduler() try: scheduler.create_new_timer(mp) except ValueError: #log or set default value pass #logging logger.info("Mediapackage with UID:%s have been reprogrammed", uid)
def get_series(): repo = context.get_repository() mhclient = context.get_mhclient() try: series_json = mhclient.getseries() repo.save_attach("series.json", series_json) except: try: series_json = repo.get_attach("series.json").read() except: series_json = '{"totalCount":"0","catalogs":[]}' series_json = json.loads(series_json) # convert JSON in ARRAY out = {} for series in series_json["catalogs"]: k = series["http://purl.org/dc/terms/"]["identifier"][0]["value"] v = series["http://purl.org/dc/terms/"]["title"][0]["value"] out[k] = v return out
def send_start(self, origin, data): mp = context.get_repository().get(data) mp.anticipated = True context.get_recorder().record(mp) self.dialog.destroy() return True
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. import time from galicaster.core import context from galicaster.mediapackage import mediapackage logger = context.get_logger() conf = context.get_conf() mhclient = context.get_mhclient() repo = context.get_repository() check_published = conf.get_boolean('retryingest', 'check_published') or True check_after = conf.get_int('retryingest', 'check_after') or 300 check_nightly = conf.get_boolean('retryingest', 'nightly') or False last_checked = time.time() logger.debug('check_published set to {}'.format(check_published)) logger.debug('check_after set to {}'.format(str(check_after))) logger.debug('check_nightly set to {}'.format(check_nightly)) def init(): try: dispatcher = context.get_dispatcher() dispatcher.connect('galicaster-notify-timer-short', reingest)
def get(id): repo = context.get_repository() response.content_type = 'text/xml' mp = repo.get(id) return set_manifest(mp)
def __init__(self, package=None): logger.info("Creating Recording Area") Gtk.Box.__init__(self) builder = Gtk.Builder() builder.add_from_file(get_ui_path('recorder.glade')) # TEST recorderui = builder.get_object("recorderbox") self.repo = context.get_repository() self.dispatcher = context.get_dispatcher() self.worker = context.get_worker() self.conf = context.get_conf() self.recorder = context.get_recorder() self.recorder.set_create_drawing_areas_func(self.create_drawing_areas) self.start_recording = False self.font = None self.scheduled_recording = False self.focus_is_active = False self.net_activity = None self.error_dialog = None # BUILD self.recorderui = builder.get_object("recorderbox") self.main_area = builder.get_object("videobox") self.vubox = builder.get_object("vubox") self.gui = builder # SWAP if not self.conf.get_boolean('basic', 'swapvideos'): self.gui.get_object("swapbutton").hide() self.swap = False # STATUS big_status = builder.get_object("bg_status") self.view = self.set_status_view() big_status.add(self.view) self.dispatcher.connect("galicaster-init", self.check_status_area) self.dispatcher.connect("galicaster-init", self.check_net) self.dispatcher.connect("net-up", self.check_net, True) self.dispatcher.connect("net-down", self.check_net, False) # VUMETER self.audiobar=Vumeter() # UI self.vubox.add(self.audiobar) self.pack_start(self.recorderui,True,True,0) # Event Manager self.dispatcher.connect("recorder-vumeter", self.audiobar.SetVumeter) self.dispatcher.connect("galicaster-status", self.event_change_mode) self.dispatcher.connect("recorder-status", self.handle_status) nb=builder.get_object("data_panel") pages = nb.get_n_pages() for index in range(pages): page=nb.get_nth_page(index) # nb.set_tab_label_packing(page, True, True,Gtk.PackType.START) # STATES self.previous = None # PERMISSIONS self.allow_pause = self.conf.get_permission("pause") self.allow_start = self.conf.get_permission("start") self.allow_stop = self.conf.get_permission("stop") self.allow_manual = self.conf.get_permission("manual") self.allow_overlap = self.conf.get_permission("overlap") # OTHER builder.connect_signals(self) self.net_activity = self.conf.get_boolean('ingest', 'active') self.proportion = 1 #TIMEOUTS deps = self.update_scheduler_deps() GObject.timeout_add(500, self.update_scheduler_timeout, *deps) self.update_clock_timeout(self.gui.get_object("local_clock")) GObject.timeout_add(10000, self.update_clock_timeout, self.gui.get_object("local_clock"))
def __init__(self, number = None,): if number == None: number = HOW_MANY parent = context.get_mainwindow() size = parent.get_size() altura = size[1] anchura = size[0] k1 = anchura / 1920.0 k2 = altura / 1080.0 gui = gtk.Builder() gui.add_from_file(get_ui_path('next.glade')) dialog = gui.get_object("dialog") table = gui.get_object("infobox") title = gui.get_object("titlelabel") okl = gui.get_object("oklabel") okb = gui.get_object("okbutton") modification = "bold "+str(int(k2*30))+"px" title.modify_font(pango.FontDescription(modification)) okl.modify_font(pango.FontDescription(modification)) # mediapackages mps=context.get_repository().get_next_mediapackages() row=1 self.dialog=dialog if parent != None: dialog.set_transient_for(parent.get_toplevel()) for mp in mps: t = self.big_label(mp.title, int(k1*30)) t.set_width_chars(int(k1*50)) t.set_line_wrap(True) # allocation = t.get_allocation() t.set_size_request( int(k1* 400) , -1 ) # FIXEME #Hack by http://tadeboro.blogspot.com/2009/05/wrapping-adn-resizing-gtklabel.html rec_time = mp.getLocalDate() if rec_time.date() == datetime.date.today(): upcoming = "Today" elif rec_time.date() == ( datetime.date.today()+datetime.timedelta(1) ): upcoming = "Tomorrow" else: upcoming = mp.getDate().strftime("%d %b %Y") # day_number month_abr year full d = self.big_label(upcoming,int(k1*30)) d.set_width_chars(20) h = self.big_label(rec_time.time().strftime("%H:%M"),int(k1*30)) h.set_width_chars(12) #l = self.big_label("Record Now", int(k1*30)) b = gtk.Button("Record Now") l = b.get_child() tamanho = pango.FontDescription(str(int(k1*25))+"px") l.modify_font(tamanho) b.set_alignment(0.5,0.5) b.set_property("tooltip-text","Record Now") b.connect("button-press-event",self.send_start, mp.identifier) b.set_property("width-request", int (k1*180)) b.set_property("height-request", int (k2*70)) table.attach(t,0,1,row-1,row,gtk.EXPAND|gtk.FILL,False,0,0) table.attach(d,1,2,row-1,row,gtk.EXPAND|gtk.FILL,False,0,0) table.attach(h,2,3,row-1,row,gtk.EXPAND|gtk.FILL,False,0,0) table.attach(b,3,4,row-1,row,gtk.EXPAND|gtk.FILL,False,0,0) t.show() h.show() d.show() b.show() row += 1 if row >= number+1 : break okb.connect("button-press-event",self.destroy) dialog.run() return None
def __init__(self, package=None): logger.info("Creating Recording Area") gtk.Box.__init__(self) builder = gtk.Builder() builder.add_from_file(get_ui_path('recorder.glade')) self.repo = context.get_repository() self.dispatcher = context.get_dispatcher() self.worker = context.get_worker() self.recorder = None self.current_mediapackage = None self.current = None self.next = None self.start_recording = False self.font = None self.scheduled_recording = False self.focus_is_active = False self.net_activity = None self.error_text = None self.error_dialog = None self.error_count = 0 self.error_handle_id = None self.ok_to_show = False self.swap_active = None self.swap = False # BUILD self.recorderui = builder.get_object("recorderbox") self.main_area = builder.get_object("videobox") self.vubox = builder.get_object("vubox") self.gui = builder # STATUS big_status = builder.get_object("bg_status") self.view = self.set_status_view() big_status.add(self.view) self.dispatcher.connect("galicaster-init", self.check_status_area) self.dispatcher.connect("galicaster-init", self.check_net) self.dispatcher.connect("restart-preview", self.check_status_area) self.dispatcher.connect("net-up", self.check_net, True) self.dispatcher.connect("net-down", self.check_net, False) # VUMETER self.audiobar = Vumeter() # UI self.vubox.add(self.audiobar) self.pack_start(self.recorderui, True, True, 0) # Event Manager self.dispatcher.connect("start-record", self.on_scheduled_start) self.dispatcher.connect("stop-record", self.on_stop) self.dispatcher.connect("start-before", self.on_start_before) self.dispatcher.connect("restart-preview", self.on_restart_preview) self.dispatcher.connect("update-rec-vumeter", self.audiobar.SetVumeter) self.dispatcher.connect("galicaster-status", self.event_change_mode) self.dispatcher.connect("galicaster-notify-quit", self.close) self.dispatcher.connect("recorder-error", self.handle_pipeline_error) nb = builder.get_object("data_panel") pages = nb.get_n_pages() for index in range(pages): page = nb.get_nth_page(index) nb.set_tab_label_packing(page, True, True, gtk.PACK_START) # STATES self.status = GC_INIT self.previous = None self.change_state(GC_INIT) self.dispatcher.connect("reload-profile", self.on_recover_from_error) # PERMISSIONS self.conf = context.get_conf() self.allow_pause = self.conf.get_permission("pause") self.allow_start = self.conf.get_permission("start") self.allow_stop = self.conf.get_permission("stop") self.allow_manual = self.conf.get_permission("manual") self.allow_overlap = self.conf.get_permission("overlap") # OTHER builder.connect_signals(self) self.net_activity = self.conf.get_boolean('ingest', 'active') self.change_state(GC_READY) self.proportion = 1 self.on_start() # SCHEDULER FEEDBACK self.scheduler_thread_id = 1 self.clock_thread_id = 1 self.scheduler_thread = thread(target=self.scheduler_launch_thread) self.clock_thread = thread(target=self.clock_launch_thread) self.scheduler_thread.daemon = True self.clock_thread.daemon = True self.scheduler_thread.start() self.clock_thread.start() # SHOW OR HIDE SWAP BUTTON if self.conf.get_boolean('basic', 'swapvideos'): self.swap_active = True else: self.swap_active = False
def __init__( self, number=None, ): if number == None: number = HOW_MANY parent = context.get_mainwindow() size = parent.get_size() altura = size[1] anchura = size[0] k1 = anchura / 1920.0 k2 = altura / 1080.0 gui = gtk.Builder() gui.add_from_file(get_ui_path('next.glade')) dialog = gui.get_object("dialog") table = gui.get_object("infobox") title = gui.get_object("titlelabel") okl = gui.get_object("oklabel") okb = gui.get_object("okbutton") width = int(size[0] / 2.5) dialog.set_default_size(width, -1) dialog.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_TOOLBAR) dialog.set_skip_taskbar_hint(True) dialog.set_modal(True) dialog.set_keep_above(False) strip = Header(size=size, title=_("Next Recordings")) title.hide() dialog.vbox.pack_start(strip, False, True, 0) dialog.vbox.reorder_child(strip, 0) strip.show() modification = "bold " + str(int(k2 * 30)) + "px" title.modify_font(pango.FontDescription(modification)) okl.modify_font(pango.FontDescription(modification)) # mediapackages mps = context.get_repository().get_next_mediapackages() row = 1 self.dialog = dialog if parent != None: dialog.set_transient_for(parent.get_toplevel()) for mp in mps: t = self.big_label(mp.title, int(k1 * 30)) t.set_width_chars(int(k1 * 50)) t.set_line_wrap(True) # allocation = t.get_allocation() t.set_size_request(int(k1 * 400), -1) # FIXEME #Hack by http://tadeboro.blogspot.com/2009/05/wrapping-adn-resizing-gtklabel.html rec_time = mp.getLocalDate() if rec_time.date() == datetime.date.today(): upcoming = "Today" elif rec_time.date() == (datetime.date.today() + datetime.timedelta(1)): upcoming = "Tomorrow" else: upcoming = mp.getDate().strftime("%d %b %Y") # day_number month_abr year full d = self.big_label(upcoming, int(k1 * 30)) d.set_width_chars(20) h = self.big_label(rec_time.time().strftime("%H:%M"), int(k1 * 30)) h.set_width_chars(12) #l = self.big_label("Record Now", int(k1*30)) b = gtk.Button(_("Record Now")) l = b.get_child() tamanho = pango.FontDescription(str(int(k1 * 25)) + "px") l.modify_font(tamanho) b.set_alignment(0.5, 0.5) b.set_property("tooltip-text", _("Record Now")) b.connect("button-press-event", self.send_start, mp.identifier) b.set_property("width-request", int(k1 * 180)) b.set_property("height-request", int(k2 * 70)) table.attach(t, 0, 1, row - 1, row, gtk.EXPAND | gtk.FILL, False, 0, 0) table.attach(d, 1, 2, row - 1, row, gtk.EXPAND | gtk.FILL, False, 0, 0) table.attach(h, 2, 3, row - 1, row, gtk.EXPAND | gtk.FILL, False, 0, 0) table.attach(b, 3, 4, row - 1, row, gtk.EXPAND | gtk.FILL, False, 0, 0) t.show() h.show() d.show() b.show() row += 1 if row >= number + 1: break okb.connect("button-press-event", self.destroy) dialog.run() return None
def change_cwd(self): repo = context.get_repository() rectemp = repo.get_rectemp_path() logger.info("Changing current working dir (cwd) to {}".format(rectemp)) os.chdir(rectemp)
def check_channels(sender, operation_code, mp): global threshold_max_volume, threshold_mean_volume # Does nothing if the operation is not INGEST if operation_code != worker.INGEST_CODE: return # Get the mediapackage mp_repo = context.get_repository() changed_mp = False working_track = None volume_left = dict({ "max" : -100, "mean" : -100}) volume_right = dict({ "max" : -100, "mean" : -100}) mpIdentifier = mp.getIdentifier() new_presenter = os.path.join(mp.getURI(),"new_presenter.mkv") logger.info('Checking audio channels tracks for MP {}'.format(mpIdentifier)) # Loop through the tracks for t in mp.getTracks(): type = t.getFlavor() logger.info(' Checking {0}'.format(type)) if type == audio_flavor: working_track = t working_file = t.getURI() working_file_duration = t.getDuration() logger.info(' working_file {}'.format(t.getURI())) # get Left Channel Details > volume_left volume_left = __detect_channel_volume(working_file, "FL") logger.info(' volume_left {}|{}|{}|'.format(volume_left, threshold_mean_volume, threshold_max_volume)) # get Right Channel Details > volume_right volume_right = __detect_channel_volume(working_file, "FR") logger.info(' volume_right {}|{}|{}|'.format(volume_right, threshold_mean_volume, threshold_max_volume)) if (volume_left['mean'] < threshold_mean_volume) and (volume_right['mean'] < threshold_mean_volume): changed_mp = False # do nothing :D logger.info("do nothing") elif (volume_left['mean'] < threshold_mean_volume): # create new presenter with right Channel logger.info('Left channel {} is below {}, using Right channel'.format(volume_left['mean'], threshold_mean_volume)) result = __execute_command([ffmpeg_bin, '-y', '-i', working_file, '-af', 'pan=mono|c0=FR', '-vc', 'copy', new_presenter ]) if (result[0] == "ERR"): logger.error("Error right channel") changed_mp = False else: changed_mp = True mp.remove(working_track, True) # remove old one first mp.add(new_presenter, mediapackage.TYPE_TRACK, flavor="presenter/source", mime="video/mkv", duration=working_file_duration) elif (volume_right['mean'] < threshold_mean_volume): # create new presenter with left Channel logger.info('Right channel {} is below {}, using Left channel'.format(volume_right['mean'], threshold_mean_volume)) result = __execute_command([ffmpeg_bin, '-y', '-i', working_file, '-af', 'pan=mono|c0=FL', '-vc', 'copy', new_presenter ]) if (result[0] == "ERR"): logger.error("Error left channel") changed_mp = False else: changed_mp = True mp.remove(working_track, True) # remove old one first mp.add(new_presenter, mediapackage.TYPE_TRACK, flavor="presenter/source", mime="video/mkv", duration=working_file_duration) # else: # logger.info("create presenter, new audio for left and other for right") # # success = True # new_audio = os.path.join(mp.getURI(),"new_audio.flac") # other_audio = os.path.join(mp.getURI(),"other_audio.flac") # # # create 3 tracks so that the muxing workflow can be used to select the correct audio # result = __execute_command([ffmpeg_bin, '-y', '-i', working_file, '-an', '-vc', 'copy', new_presenter ]) # if (result[0] == "ERR"): # logger.error("Error presenter") # success = False # else: # mp.add(new_presenter, mediapackage.TYPE_TRACK, flavor="presenter/source", mime="video/mkv", duration=working_file_duration) # # result = __execute_command([ffmpeg_bin, '-y', '-i', working_file, '-vn', '-af', 'pan=mono|c0=FL', new_audio ]) # if (result[0] == "ERR"): # logger.error("Error audio") # success = False # else: # mp.add(new_audio, mediapackage.TYPE_TRACK, flavor="presenter/source", mime="audio/flac", duration=working_file_duration) # # result = __execute_command([ffmpeg_bin, '-y', '-i', working_file, '-vn', '-af', 'pan=mono|c0=FR', other_audio ]) # if (result[0] == "ERR"): # logger.error("Error other audio") # success = False # else: # mp.add(other_audio, mediapackage.TYPE_TRACK, flavor="other/audio", mime="audio/flac", duration=working_file_duration) # # changed_mp = success # if success: # mp.remove(working_track, True) # remove old one first # logger.info('Constructing new presenter, audio and other') # Update the MP if something changed if changed_mp: logger.info('Updating MP {}'.format(mpIdentifier)) mp.add(working_file, mediapackage.TYPE_TRACK, flavor="other/video", mime="video/mkv", duration=working_file_duration) # original stereo is preserved and ingested mp_repo.update(mp) logger.info("Finished")
def __init__(self, key, parent=None): if not parent: parent = context.get_mainwindow() size = context.get_mainwindow().get_size() self.wprop = size[0] / 1920.0 self.hprop = size[1] / 1080.0 width = int(size[0] / 3) height = int(size[1] / 2.5) gtk.Window.__init__(self) self.set_title("Mediapackage Info ") self.set_position(gtk.WIN_POS_CENTER_ALWAYS) self.set_default_size(width, height) self.set_modal(True) if parent: self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) self.set_transient_for(parent) self.set_destroy_with_parent(True) # INFO mp = context.get_repository().get(key) # Basic info basic, basic_table = self.add_framed_table("Basic") self.add_data(basic_table, "Title:", mp.title) self.add_data(basic_table, "Identifier:", mp.identifier) self.add_data(basic_table, "Folder:", mp.getURI()) self.add_data(basic_table, "Duration:", readable.time(int(mp.getDuration()) / 1000)) self.add_data(basic_table, "Size:", readable.size(mp.getSize())) self.add_data( basic_table, "Recorded on:", readable.date(mp.getStartDateAsString(), "%B %d, %Y - %H:%M").replace(' 0', ' ')) # Operations info ops, ops_table = self.add_framed_table("Operations") self.add_data(ops_table, "Ingest:", mediapackage.op_status[mp.getOpStatus("ingest")]) self.add_data(ops_table, "Zipping:", mediapackage.op_status[mp.getOpStatus("exporttozip")]) self.add_data(ops_table, "Side by Side:", mediapackage.op_status[mp.getOpStatus("sidebyside")]) # Series info if mp.getSeries(): series, series_table = self.add_framed_table("Series") self.add_data(series_table, "Series:", mp.getSeriesTitle()) self.add_data(series_table, "Identifier:", mp.getSeries()) # Track info tracks, track_table = self.add_framed_table("Tracks", True) first = True for track in mp.getTracks(): if not first: self.add_data(track_table, "", "") first = False self.add_data(track_table, "Name:", track.getIdentifier()) self.add_data(track_table, "Flavor:", track.getFlavor()) self.add_data(track_table, "Type:", track.getMimeType()) filename = str(path.split(track.getURI())[1]) self.add_data(track_table, "File:", filename) # Catalog info cats, cat_table = self.add_framed_table("Catalogs", True) first = True for cat in mp.getCatalogs(): if not first: self.add_data(cat_table, "", "") first = False self.add_data(cat_table, "Name:", cat.getIdentifier()) self.add_data(cat_table, "Flavor:", cat.getFlavor()) self.add_data(cat_table, "Type:", cat.getMimeType()) filename = str(path.split(cat.getURI())[1]) self.add_data(cat_table, "File:", filename) #PACKING box = gtk.VBox() box.pack_start(basic, False, True, int(self.hprop * 10)) box.pack_start(ops, False, True, int(self.hprop * 10)) if mp.getSeries(): box.pack_start(series, False, True, int(self.hprop * 10)) box.pack_start(tracks, False, True, int(self.hprop * 10)) box.pack_start(cats, False, True, int(self.hprop * 10)) external_align = gtk.Alignment(0.5, 0, 0.8, 0.8) external_align.add(box) self.add(external_align) #BUTTONS self.buttons = gtk.HButtonBox() self.buttons.set_layout(gtk.BUTTONBOX_SPREAD) self.add_button("Close", self.close) self.add_button("Open Folder", self.openfolder, mp.getURI()) box.pack_end(self.buttons, False, False, int(self.hprop * 10)) self.show_all()
def __init__(self, package=None): logger.info("Creating Recording Area") Gtk.Box.__init__(self) builder = Gtk.Builder() builder.add_from_file(get_ui_path('recorder.glade')) release = builder.get_object("release_label") release.set_label(get_footer()) # TEST self.repo = context.get_repository() self.dispatcher = context.get_dispatcher() self.worker = context.get_worker() self.conf = context.get_conf() self.recorder = context.get_recorder() self.recorder.set_create_drawing_areas_func(self.create_drawing_areas) self.start_recording = False self.font = None self.scheduled_recording = False self.focus_is_active = False self.net_activity = None self.error_dialog = None self.close_before_response_action = False # BUILD self.recorderui = builder.get_object("recorderbox") self.main_area = builder.get_object("videobox") self.vubox = builder.get_object("vubox") self.gui = builder # VUMETER self.rangeVum = 50 self.thresholdVum = self.conf.get_float('audio', 'min') self.mute = False self.stereo = True self.vumeterL = builder.get_object("progressbarL") self.vumeterR = builder.get_object("progressbarR") self.label_channels = builder.get_object("label_channels") self.low_audio = False self.lowaudio_threshold = self.conf.get_float('lowaudio', 'lowaudio_threshold') # SWAP if not self.conf.get_boolean('basic', 'swapvideos'): self.gui.get_object("swapbutton").destroy() self.swap = False # STATUS self.view = self.set_status_view() hbox1 = self.gui.get_object('hbox1') hbox1.add(self.view) self.dispatcher.connect_ui("init", self.check_status_area) self.dispatcher.connect_ui("init", self.check_net, None) self.dispatcher.connect_ui("opencast-status", self.check_net) self.dispatcher.connect_ui("operation-started", self.check_operations) self.dispatcher.connect_ui("operation-stopped", self.check_operations) # UI self.pack_start(self.recorderui, True, True, 0) self.pause_dialog = None # Event Manager self.dispatcher.connect_ui("recorder-vumeter", self.set_vumeter) self.dispatcher.connect_ui("view-changed", self.event_change_mode) self.dispatcher.connect_ui("recorder-status", self.handle_status) self.dispatcher.connect_ui("recorder-ready", self.reset_mute) # STATES self.previous = None # PERMISSIONS self.allow_pause = self.conf.get_permission("pause") self.allow_start = self.conf.get_permission("start") self.allow_stop = self.conf.get_permission("stop") self.allow_manual = self.conf.get_permission("manual") self.allow_overlap = self.conf.get_permission("overlap") self.help_main_str = self.conf.get('help', 'main') self.help_text_str = self.conf.get('help', 'text') # OTHER builder.connect_signals(self) self.net_activity = self.conf.get_boolean('ingest', 'active') self.pausedialog_size = self.conf.get_int('basic', 'pausedialog_size', default=15) if self.pausedialog_size < 5: self.pausedialog_size = 5 elif self.pausedialog_size > 100: self.pausedialog_size = 100 self.proportion = 1 #TIMEOUTS deps = self.update_scheduler_deps() GObject.timeout_add(500, self.update_scheduler_timeout, *deps) self.update_clock_timeout(self.gui.get_object("local_clock")) GObject.timeout_add(10000, self.update_clock_timeout, self.gui.get_object("local_clock"))
def drop_presentations(sender, operation_code, mp): # Does nothing if the operation is not INGEST if operation_code != worker.INGEST_CODE: return # Get the mediapackage mp_repo = context.get_repository() track_p1 = None track_p2 = None bitrate_p1 = 0 bitrate_p2 = 0 size_p1 = 0 size_p2 = 0 removed = False mpIdentifier = mp.getIdentifier() logger.info('Checking presentation tracks for MP ' + mpIdentifier) # Get track size and bitrates for t in mp.getTracks(): type = t.getFlavor() if type == flavor_p1 or type == flavor_p2: # ffprobe -v error -show_entries format=bit_rate -of default=noprint_wrappers=1 presentation.avi # Output: "bit_rate=59565" bitrate = 0 size = 0 size = os.path.getsize(t.getURI()) try: ff_bitrate = subprocess.check_output([ ffprobe_bin, '-v', 'error', '-show_entries', 'format=bit_rate', '-of', 'default=nokey=1:noprint_wrappers=1', t.getURI() ]).replace("\n", "") bitrate = int(ff_bitrate) logger.info('bitrate for track %s: %i bps', t.getURI(), bitrate) except ValueError: # ffprobe will return "N/A" for unknown bitrate (where the file was not closed properly) logger.info('Unknown bitrate for track %s: %s', t.getURI(), ff_bitrate) bitrate = -1 except subprocess.CalledProcessError: logger.info('%s unable to determine bitrate for track %s', ffprobe_bin, t.getURI()) bitrate = -1 if type == flavor_p1: track_p1 = t bitrate_p1 = bitrate size_p1 = size if type == flavor_p2: track_p2 = t bitrate_p2 = bitrate size_p2 = size # Remove tracks with size below lower threshold if (track_p1 is not None) and (size_p1 < size_lower_threshold): mp.remove(track_p1, True) removed = True bitrate_p1 = 0 logger.info( 'Presentation track %s is smaller than %i bytes and has been removed', os.path.basename(track_p1.getURI()), size_lower_threshold) if (track_p2 is not None) and (size_p2 < size_lower_threshold): mp.remove(track_p2, True) removed = True bitrate_p2 = 0 logger.info( 'Presentation track %s is smaller than %i bytes and has been removed', os.path.basename(track_p2.getURI()), size_lower_threshold) # Remove tracks with bitrate below lower threshold if (bitrate_p1 > 0) and (bitrate_p1 < bitrate_lower_threshold): mp.remove(track_p1, True) removed = True logger.info('Presentation track ' + os.path.basename(track_p1.getURI()) + ' is probably empty and has been removed') if (bitrate_p2 > 0) and (bitrate_p2 < bitrate_lower_threshold): mp.remove(track_p2, True) removed = True logger.info('Presentation track ' + os.path.basename(track_p2.getURI()) + ' is probably empty and has been removed') # If the bitrate is unknown or intermediate, check percentage empty if (bitrate_p1 < 0) or ((bitrate_p1 >= bitrate_lower_threshold) and (bitrate_p1 < bitrate_upper_threshold)): empty_p1 = __track_empty(track_p1) if (empty_p1 >= empty_threshold): mp.remove(track_p1, True) removed = True logger.info( 'Presentation track ' + os.path.basename(track_p1.getURI()) + ' is %i%% empty and has been removed', empty_p1) if (bitrate_p2 < 0) or ((bitrate_p2 >= bitrate_lower_threshold) and (bitrate_p2 < bitrate_upper_threshold)): empty_p2 = __track_empty(track_p2) if (empty_p2 >= empty_threshold): mp.remove(track_p2, True) removed = True logger.info( 'Presentation track ' + os.path.basename(track_p2.getURI()) + ' is %i%% empty and has been removed', empty_p2) # Check for duplicate tracks if (removed == False) and (bitrate_p1 > 0) and (bitrate_p2 > 0): bitrate_diff = abs(bitrate_p1 - bitrate_p2) / float( min(bitrate_p1, bitrate_p2)) logger.info('Presentation track bitrates vary by %.3f%%', bitrate_diff * 100) if (bitrate_diff <= bitrate_diff_match_threshold): logger.info( 'Presentation files have very similar bitrates: removing %s', os.path.basename(track_p2.getURI())) mp.remove(track_p2, True) removed = True if (bitrate_diff > bitrate_diff_match_threshold) and ( bitrate_diff < bitrate_diff_threshold): logger.info( 'Presentation files have similar bitrates: comparing content') match_result_i = 0 try: match_result = subprocess.check_output( [videomatch_bin, track_p1.getURI(), track_p2.getURI()]).replace("\n", "") match_result_i = int(match_result) logger.info( 'Frame similarity between presentation tracks: %i%%', match_result_i) except ValueError: logger.info('Unknown frame similarity result: %s', match_result) except subprocess.CalledProcessError: logger.info('Unable to compare frame similarity with %s', videomatch_bin) if (match_result_i) >= similarity_threshold: logger.info( 'Presentation files are substantially the same (%s%%): removing %s', match_result, os.path.basename(track_p2.getURI())) mp.remove(track_p2, True) removed = True # Update the MP if something changed if removed: logger.info('Updating MP ' + mpIdentifier) mp_repo.update(mp) logger.info("Finished")
def __init__(self, package=None): logger.info("Creating Recording Area") gtk.Box.__init__(self) builder = gtk.Builder() builder.add_from_file(get_ui_path('recorder.glade')) self.repo = context.get_repository() self.dispatcher = context.get_dispatcher() self.worker = context.get_worker() self.recorder = None self.current_mediapackage = None self.current = None self.next = None self.restarting = False self.font = None self.scheduled_recording = False self.focus_is_active = False self.net_activity = None self.error_id = None self.error_text = None self.error_dialog = None self.ok_to_show = False self.swap_active = None self.swap = False # BUILD self.recorderui = builder.get_object("recorderbox") self.main_area = builder.get_object("videobox") self.vubox = builder.get_object("vubox") self.gui = builder # BIG STATUS big_status = builder.get_object("bg_status") self.view = self.set_status_view() big_status.add(self.view) # STATUS BAR self.statusbar=status_bar.StatusBarClass() self.dispatcher.connect("update-rec-status", self.statusbar.SetStatus) self.dispatcher.connect("update-video", self.statusbar.SetVideo) self.dispatcher.connect("galicaster-init", self.check_status_area) self.dispatcher.connect("galicaster-init", self.check_net) self.dispatcher.connect("restart-preview", self.check_status_area) self.dispatcher.connect("net-up", self.check_net, True) self.dispatcher.connect("net-down", self.check_net, False) self.statusbar.SetTimer(0) # VUMETER self.audiobar=Vumeter() # UI self.vubox.add(self.audiobar) self.pack_start(self.recorderui,True,True,0) # Event Manager self.dispatcher.connect("start-record", self.on_scheduled_start) self.dispatcher.connect("stop-record", self.on_stop) self.dispatcher.connect("start-before", self.on_start_before) self.dispatcher.connect("restart-preview", self.on_restart_preview) self.dispatcher.connect("update-rec-vumeter", self.audiobar.SetVumeter) self.dispatcher.connect("galicaster-status", self.event_change_mode) self.dispatcher.connect("galicaster-notify-quit", self.close) nb=builder.get_object("data_panel") pages = nb.get_n_pages() for index in range(pages): page=nb.get_nth_page(index) nb.set_tab_label_packing(page, True, True,gtk.PACK_START) # STATES self.status = GC_INIT self.previous = None self.change_state(GC_INIT) self.dispatcher.connect("reload-profile", self.on_recover_from_error) # PERMISSIONS self.conf = context.get_conf() self.allow_pause = self.conf.get_permission("pause") self.allow_start = self.conf.get_permission("start") self.allow_stop = self.conf.get_permission("stop") self.allow_manual = self.conf.get_permission("manual") self.allow_overlap = self.conf.get_permission("overlap") # OTHER builder.connect_signals(self) self.net_activity = self.conf.get_boolean('ingest', 'active') self.change_state(GC_READY) self.proportion = 1 self.on_start() # SCHEDULER FEEDBACK self.scheduler_thread_id = 1 self.clock_thread_id = 1 self.start_thread_id = None self.scheduler_thread = thread(target=self.scheduler_launch_thread) self.clock_thread = thread(target=self.clock_launch_thread) self.scheduler_thread.daemon = True self.clock_thread.daemon = True self.scheduler_thread.start() self.clock_thread.start() self.dispatcher.emit("galicaster-init") # SHOW OR HIDE SWAP BUTTON if self.conf.get_boolean('basic', 'swapvideos'): self.swap_active = True else: self.swap_active = False
def __init__(self, key, parent = None): if not parent: parent = context.get_mainwindow() size = context.get_mainwindow().get_size() self.wprop = size[0]/1920.0 self.hprop = size[1]/1080.0 width = int(size[0]/3) height = int(size[1]/2.5) gtk.Window.__init__(self) self.set_title(_("Mediapackage Info ")) self.set_position(gtk.WIN_POS_CENTER_ALWAYS) self.set_default_size(width,height) self.set_modal(True) if parent: self.set_type_hint(gtk.gdk.WINDOW_TYPE_HINT_DIALOG) self.set_transient_for(parent) self.set_destroy_with_parent(True) # INFO mp = context.get_repository().get(key) # Metadata info data = {} data['title'] = mp.title data['identifier'] = mp.identifier data['folder'] = mp.getURI() data['duration'] = readable.time(int(mp.getDuration())/1000) data['size'] = readable.size(mp.getSize()) data['created'] = readable.date(mp.getStartDateAsString(), "%B %d, %Y - %H:%M").replace(' 0',' ') basic, basic_table = self.add_framed_table(_("Basic")) for item in ORDER_EPISODES: if item in data: self.add_data(basic_table,EPISODE_NAMES[item], data[item]) elif item in mp.metadata_episode: self.add_data(basic_table,EPISODE_NAMES[item], mp.metadata_episode[item]) # Operations info ops, ops_table = self.add_framed_table(_("Operations")) self.add_data(ops_table,_("Ingest:"), mediapackage.op_status[mp.getOpStatus("ingest")]) self.add_data(ops_table,_("Zipping:"), mediapackage.op_status[mp.getOpStatus("exporttozip")]) self.add_data(ops_table,_("Side by Side:"), mediapackage.op_status[mp.getOpStatus("sidebyside")]) # Series info if mp.getSeries(): series, series_table = self.add_framed_table(_("Series")) for item in ORDER_SERIES: if item in mp.metadata_series: self.add_data(series_table,SERIES_NAMES[item], mp.metadata_series[item]) # Track info tracks, track_table = self.add_framed_table(_("Tracks"), True) first = True for track in mp.getTracks(): if not first: self.add_data(track_table,"","") first = False self.add_data(track_table,_("Name:"),track.getIdentifier()) self.add_data(track_table,_("Flavor:"),track.getFlavor()) self.add_data(track_table,_("Type:"),track.getMimeType()) filename = str(path.split(track.getURI())[1]) self.add_data(track_table,_("File:"),filename) # Catalog info cats, cat_table = self.add_framed_table(_("Catalogs"), True) first = True for cat in mp.getCatalogs(): if not first: self.add_data(cat_table,"","") first = False self.add_data(cat_table,_("Name:"),cat.getIdentifier()) self.add_data(cat_table,_("Flavor:"),cat.getFlavor()) self.add_data(cat_table,_("Type:"),cat.getMimeType()) filename = str(path.split(cat.getURI())[1]) self.add_data(cat_table,_("File:"),filename) #PACKING box = gtk.VBox() box.pack_start(basic,False,True,int(self.hprop*10)) box.pack_start(ops,False,True,int(self.hprop*10)) if mp.getSeries(): box.pack_start(series,False,True,int(self.hprop*10)) box.pack_start(tracks,False,True,int(self.hprop*10)) box.pack_start(cats,False,True,int(self.hprop*10)) external_align=gtk.Alignment(0.5,0,0.8,0.8) external_align.add(box) self.add(external_align) #BUTTONS self.buttons = gtk.HButtonBox() self.buttons.set_layout(gtk.BUTTONBOX_SPREAD) self.add_button(_("Close"), self.close) self.add_button(_("Open Folder"), self.openfolder, mp.getURI()) box.pack_end(self.buttons, False, False, int(self.hprop*10)) self.show_all()
def test_context(self): self.assertNotEqual(type(context.get_conf()), galicaster.core.conf) self.assertEqual(type(context.get_conf()), galicaster.core.conf.Conf) self.assertEqual(type(context.get_dispatcher()), galicaster.core.dispatcher.Dispatcher) self.assertEqual(type(context.get_repository()), galicaster.mediapackage.repository.Repository)
def __init__(self, package=None): logger.info("Creating Recording Area") Gtk.Box.__init__(self) builder = Gtk.Builder() builder.add_from_file(get_ui_path('recorder.glade')) release = builder.get_object("release_label") release.set_label(get_footer()) # TEST self.repo = context.get_repository() self.dispatcher = context.get_dispatcher() self.worker = context.get_worker() self.conf = context.get_conf() self.recorder = context.get_recorder() self.recorder.set_create_drawing_areas_func(self.create_drawing_areas) self.start_recording = False self.font = None self.scheduled_recording = False self.focus_is_active = False self.net_activity = None self.error_dialog = None self.close_before_response_action = False # BUILD self.recorderui = builder.get_object("recorderbox") self.main_area = builder.get_object("videobox") self.vubox = builder.get_object("vubox") self.gui = builder # VUMETER self.rangeVum = 50 self.thresholdVum = self.conf.get_float('audio','min') self.mute = False self.stereo = True self.vumeterL = builder.get_object("progressbarL") self.vumeterR = builder.get_object("progressbarR") self.label_channels= builder.get_object("label_channels") self.low_audio = False self.lowaudio_threshold = self.conf.get_float('lowaudio','lowaudio_threshold') # SWAP if not self.conf.get_boolean('basic', 'swapvideos'): self.gui.get_object("swapbutton").destroy() self.swap = False # STATUS self.view = self.set_status_view() hbox1 = self.gui.get_object('hbox1') hbox1.add(self.view) self.dispatcher.connect_ui("init", self.check_status_area) self.dispatcher.connect_ui("init", self.check_net, None) self.dispatcher.connect_ui("opencast-status", self.check_net) self.dispatcher.connect_ui("operation-started", self.check_operations) self.dispatcher.connect_ui("operation-stopped", self.check_operations) # UI self.pack_start(self.recorderui,True,True,0) self.pause_dialog = None # Event Manager self.dispatcher.connect_ui("recorder-vumeter", self.set_vumeter) self.dispatcher.connect_ui("view-changed", self.event_change_mode) self.dispatcher.connect_ui("recorder-status", self.handle_status) self.dispatcher.connect_ui("recorder-ready", self.reset_mute) # STATES self.previous = None # PERMISSIONS self.allow_pause = self.conf.get_permission("pause") self.allow_start = self.conf.get_permission("start") self.allow_stop = self.conf.get_permission("stop") self.allow_manual = self.conf.get_permission("manual") self.allow_overlap = self.conf.get_permission("overlap") self.help_main_str = self.conf.get('help', 'main') self.help_text_str = self.conf.get('help', 'text') # OTHER builder.connect_signals(self) self.net_activity = self.conf.get_boolean('ingest', 'active') self.pausedialog_size = self.conf.get_int('basic', 'pausedialog_size', default=15) if self.pausedialog_size < 5: self.pausedialog_size = 5 elif self.pausedialog_size > 100: self.pausedialog_size = 100 self.proportion = 1 #TIMEOUTS deps = self.update_scheduler_deps() GObject.timeout_add(500, self.update_scheduler_timeout, *deps) self.update_clock_timeout(self.gui.get_object("local_clock")) GObject.timeout_add(10000, self.update_clock_timeout, self.gui.get_object("local_clock"))
def send_start(self,origin, event, data): mp = context.get_repository().get(data) context.get_recorder().record(mp) self.dialog.destroy() return True
def init(): global repo repo = context.get_repository() restp = threading.Thread(target=run, kwargs={'host':'0.0.0.0', 'port':8080}) restp.setDaemon(True) restp.start()