Example #1
0
    def __init__(self, package=None):
        ManagerUI.__init__(self, 1)
        builder = gtk.Builder()
        builder.add_from_file(get_ui_path('player.glade'))
        self.gui = builder

        # BUILD GUI
        self.playerui = builder.get_object("playerbox")
        self.main_area = builder.get_object("videobox")
        self.player = None

        # Seek Bar
        self.duration = 0
        self.seeking = False
        self.jump = 0  # seek value
        self.jump_id = 0  # seek signal id
        self.correct = False  # To correct SCROLL_JUMP, after release it
        self.seek_bar = self.gui.get_object("seekbar")
        self.seek_bar.add_events(gtk.gdk.SCROLL_MASK)
        self.seek_bar.connect("change-value", self.on_seek)

        # VUMETER
        self.audiobar = AudioBarClass()
        self.volume_bar = self.audiobar.volume
        self.volume_bar.connect("value-changed", self.on_volume2)

        self.vubox = builder.get_object("vubox")
        self.vubox.add(self.audiobar.bar)

        # STATUSBAR
        self.statusbar = StatusBarClass()
        sbox = builder.get_object("statusbox")
        sbox.add(self.statusbar.bar)

        self.playerui.pack_start(self.strip, False, False, 0)
        self.playerui.reorder_child(self.strip, 0)
        self.pack_start(self.playerui, True, True, 0)

        self.status = GC_INIT
        self.previous = None
        self.change_state(GC_INIT)
        self.mediapackage = None  # The Mediapackage being reproduced

        self.thread_id = None
        builder.connect_signals(self)

        self.dispatcher.connect("update-play-vumeter",
                                self.audiobar.SetVumeter)
        self.dispatcher.connect("play-stopped", self.change_state_bypass,
                                GC_READY)
        self.dispatcher.connect('play-list', self.play_from_list)
        self.dispatcher.connect("galicaster-status", self.event_change_mode)
        self.dispatcher.connect("galicaster-notify-quit", self.close)
Example #2
0
    def __init__(self,package=None):       
        ManagerUI.__init__(self,1)
	builder = gtk.Builder()
        builder.add_from_file(get_ui_path('player.glade'))
        self.gui=builder

        # BUILD GUI
        self.playerui = builder.get_object("playerbox")
        self.main_area = builder.get_object("videobox")
        self.player = None

        # Seek Bar
        self.duration = 0
        self.seeking = False
        self.jump=0 # seek value
        self.jump_id=0 # seek signal id
        self.correct=False # To correct SCROLL_JUMP, after release it
        self.seek_bar=self.gui.get_object("seekbar")
        self.seek_bar.add_events(gtk.gdk.SCROLL_MASK)
        self.seek_bar.connect("change-value", self.on_seek) 
      
        # VUMETER
        self.audiobar=AudioBarClass()
        self.volume_bar = self.audiobar.volume
        self.volume_bar.connect("value-changed", self.on_volume2) 

        self.vubox = builder.get_object("vubox")
        self.vubox.add(self.audiobar.bar)

        # STATUSBAR
        self.statusbar= StatusBarClass()
        sbox = builder.get_object("statusbox")
        sbox.add(self.statusbar.bar)
      
        self.playerui.pack_start(self.strip,False,False,0)
        self.playerui.reorder_child(self.strip,0)
        self.pack_start(self.playerui,True,True,0)

        self.status=GC_INIT
        self.previous=None
        self.change_state(GC_INIT)
        self.mediapackage=None # The Mediapackage being reproduced

        self.thread_id=None
        builder.connect_signals(self)

        self.dispatcher.connect("update-play-vumeter", self.audiobar.SetVumeter)
        self.dispatcher.connect("play-stopped", self.change_state_bypass, GC_READY)
        self.dispatcher.connect('play-list', self.play_from_list)
        self.dispatcher.connect("galicaster-status", self.event_change_mode)
        self.dispatcher.connect("galicaster-notify-quit", self.close)
Example #3
0
    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.record = 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.start_id = None

        # BUILD
        self.recorderui = builder.get_object("recorderbox")
        self.main_area = builder.get_object("videobox")
        self.areas = None
        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 = AudioBarClass()

        # UI
        self.vubox.add(self.audiobar.bar)
        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_scheduled_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")
Example #4
0
class RecorderClassUI(gtk.Box):
    """
    Graphic User Interface for Record alone
    """

    __gtype_name__ = 'RecorderClass'

    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.record = 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.start_id = None

        # BUILD
        self.recorderui = builder.get_object("recorderbox")
        self.main_area = builder.get_object("videobox")
        self.areas = None
        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 = AudioBarClass()

        # UI
        self.vubox.add(self.audiobar.bar)
        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_scheduled_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")

    def select_devices(self):
        """Loads the bins and creates the preview areas for the active profile, creating a new mediapacakge."""
        logger.info("Setting Devices the new way")
        self.mediapackage = mediapackage.Mediapackage()
        self.mediapackage.setTitle("Recording started at " +
                                   datetime.datetime.now().replace(
                                       microsecond=0).isoformat())
        current_profile = self.conf.get_current_profile()
        bins = current_profile.tracks

        for objectbin in bins:
            objectbin['path'] = self.repo.get_attach_path()
        devices = current_profile.get_video_areas()
        areas = self.create_drawing_areas(devices)

        self.error_text = None
        self.error_dialog = None
        if self.error_id:
            logger.info("Error in select devices " + str(self.error_id))
            self.dispatcher.disconnect(self.error_id)
        self.error_id = self.dispatcher.connect("recorder-error",
                                                self.handle_pipeline_error)
        self.audiobar.ClearVumeter()
        self.record = Recorder(bins, areas)
        self.record.mute_preview(not self.focus_is_active)
        return True

    #  ------------------------- PLAYER ACTIONS ------------------------

    def on_start(self, button=None):
        """Preview at start - Galicaster initialization"""
        logger.info("Starting Preview")
        self.conf.reload()

        #self.start_id = self.dispatcher.connect("start-preview", self.on_start_button)
        self.on_start_button()
        return True

    def on_start_button(self, button=None):
        """Triggers bin loading and start preview"""
        self.select_devices()
        #self.dispatcher.disconnect(self.start_id)
        #self.start_id = None
        ok = self.record.preview()
        if ok:
            if self.mediapackage.manual:
                self.mediapackage.setTitle("Recording started at " +
                                           datetime.datetime.now().replace(
                                               microsecond=0).isoformat())
                self.change_state(GC_PREVIEW)
            else:
                self.change_state(GC_ERROR)

    def on_restart_preview(self, button=None, element=None):
        """Restarting preview, commanded by record"""
        logger.info("Restarting Preview")
        self.conf.reload()
        ok = self.select_devices()
        if ok:
            self.record.preview()
            self.change_state(GC_PREVIEW)
        else:
            logger.error("Restarting Preview Failed")
            self.change_state(GC_ERROR)
        self.restarting = False
        return True

    def on_rec(self, button=None):
        """Manual Recording """
        logger.info("Recording")
        self.dispatcher.emit("starting-record")
        self.record.record()
        self.mediapackage.status = mediapackage.RECORDING
        self.mediapackage.setDate(
            datetime.datetime.utcnow().replace(microsecond=0))
        self.clock = self.record.get_clock()
        self.timer_thread_id = 1
        self.timer_thread = thread(target=self.timer_launch_thread)
        self.timer_thread.daemon = True
        self.timer_thread.start()
        self.change_state(GC_RECORDING)
        return True

    def on_start_before(self, origin, key):
        """ Start a recording before its schedule """
        logger.info("Start recording before schedule")
        self.mediapackage = self.repo.get(key)
        self.mediapackage.manual = True
        self.on_rec()
        return True

    def on_pause(self, button):
        """Pauses or resumes a recording"""
        if self.status == GC_PAUSED:
            logger.debug("Resuming Recording")
            self.change_state(GC_RECORDING)
            self.record.resume()
        elif self.status == GC_RECORDING:
            logger.debug("Pausing Recording")
            self.change_state(GC_PAUSED)
            self.record.pause()
            gui = gtk.Builder()
            gui.add_from_file(get_ui_path("paused.glade"))
            dialog = gui.get_object("dialog")
            self.pause_dialog = dialog
            #image = gui.get_object("image")
            button = gui.get_object("button")
            dialog.set_transient_for(self.get_toplevel())

            response = dialog.run()
            if response == 1:
                self.on_pause(None)
            dialog.destroy()

    def on_stop(self, button):
        """Stops preview or recording and closes the Mediapakage"""
        if self.conf.get_boolean("basic", "stopdialog"):
            text = {
                "title": "Stop",
                "main": "Are you sure you want to\nstop the recording?",
            }
            buttons = ("Stop", gtk.RESPONSE_OK, gtk.STOCK_CANCEL,
                       gtk.RESPONSE_REJECT)
            warning = message.PopUp(message.WARNING, text,
                                    context.get_mainwindow(), buttons)

            if warning.response not in message.POSITIVE:
                return False

        if self.scheduled_recording:
            self.current_mediapackage = None
            self.current = None
            self.scheduled_recording = False
        self.close_recording()

    def close_recording(self):
        """Set the final data on the mediapackage, stop the record and restart the preview"""
        close_duration = (self.clock.get_time() -
                          self.initial_time) * 1000 / gst.SECOND
        # To avoid error messages on stopping pipelines
        if self.error_dialog:
            self.error_dialog.destroy()
            self.error_dialog = None
        self.record.stop_record_and_restart_preview()
        self.change_state(GC_STOP)

        self.mediapackage.status = mediapackage.RECORDED
        self.mediapackage.properties['origin'] = self.conf.hostname
        self.repo.add_after_rec(self.mediapackage, self.record.bins_desc,
                                close_duration)

        code = 'manual' if self.mediapackage.manual else 'scheduled'
        if self.conf.get_lower('ingest', code) == 'immediately':
            self.worker.ingest(self.mediapackage)
        elif self.conf.get_lower('ingest', code) == 'nightly':
            self.worker.ingest_nightly(self.mediapackage)

        self.timer_thread_id = None

    def on_scheduled_start(self, source, identifier):
        """Starts a scheduled recording, replacing the mediapackage in use"""
        logger.info("Scheduled Start")
        self.conf.reload()
        self.current_mediapackage = identifier
        self.scheduled_recording = True
        a = thread(target=self.start_thread, args=(identifier, ))
        a.daemon = False
        a.start()

    def start_thread(self, identifier):
        """Thread handling a scheduled recording"""
        self.start_thread_id = 1

        if self.status == GC_PREVIEW:  # Record directly
            self.mediapackage = self.repo.get(self.current_mediapackage)
            self.on_rec()

        elif self.status in [GC_RECORDING, GC_PAUSED]:

            if self.allow_overlap:
                pass
                # TODO: dont stop and extend recording until the end of the new interval
                # In case of stop, restart with the overlapped job

            else:  # Stop current recording, wait until prewiew restarted and record
                self.restarting = True
                self.close_recording()
                while self.restarting:
                    time.sleep(0.1)
                    if self.start_thread_id == None:
                        return
                self.mediapackage = self.repo.get(self.current_mediapackage)
                self.on_rec()

        elif self.status == GC_INIT:  # Start Preview and Record
            self.on_start_button()
            while self.record.get_status() != gst.STATE_PLAYING:
                time.sleep(0.2)
                if self.start_thread_id == None:
                    return
            self.mediapackage = self.repo.get(self.current_mediapackage)
            self.on_rec()

        title = self.repo.get(identifier).title
        self.dispatcher.emit("update-video", title)

        return None

    def on_scheduled_stop(self, source, identifier):
        """Updates the mediapackage information after a scheduled recoring."""
        logger.info("Scheduled Stop")
        self.current_mediapackage = None
        self.current = None
        self.close_recording()
        self.scheduled_recording = False

    def reload_state_and_permissions(self):
        """Force a state review in case permissions had changed."""
        self.conf.reload()
        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.change_state(self.status)

    def reload_state(self):
        """Force a state review in case situation had changed"""
        self.change_state(self.status)

    def on_help(self, button):
        """Triggers a pop-up when Help button is clicked"""
        logger.info("Help requested")

        text = {
            "title": "Help",
            "main": " Visit galicaster.teltek.es",
            "text": " ...or contact us on our community list."
        }
        buttons = None
        message.PopUp(message.INFO, text, context.get_mainwindow(), buttons)

    def restart(self):  # FIXME name confusing cause on_restart_preview
        """Called by Core, if in preview, reload configuration and restart preview."""
        if self.status == GC_STOP:
            self.on_start()

        elif self.status == GC_PREVIEW:
            self.change_state(GC_STOP)
            self.record.just_restart_preview()
        else:
            logger.warning("Restart preview called while Recording")

        return True

    def handle_pipeline_error(self, origin, error_message):
        """ Captures a pipeline error.
        If the recording are is active, shows it
        """
        self.change_state(GC_ERROR)
        if self.error_id:
            self.dispatcher.disconnect(self.error_id)
            self.error_id = None

        self.error_text = error_message
        if self.focus_is_active:
            self.launch_error_message(error_message)

    def launch_error_message(self, error_message):
        """Shows an active error message."""
        text = {
            "title": "Recorder",
            "main":
            " Please review your configuration \nor load another profile",
            "text": error_message
        }
        buttons = None
        logger.error("ERROR: " + error_message)
        self.error_dialog = message.PopUp(message.ERROR, text,
                                          context.get_mainwindow(), buttons)

    def on_recover_from_error(self, origin):
        """If an error ocurred, removes preview areas and disconnect error handlers."""
        if self.status in [GC_ERROR, GC_STOP]:
            main = self.main_area
            for child in main.get_children():
                main.remove(child)
                child.destroy()
            self.change_state(GC_INIT)
            self.on_start()

        elif self.status in [GC_PREVIEW, GC_PRE2]:
            #self.restart()
            self.record.stop_preview()
            self.dispatcher.disconnect(self.error_id)
            self.error_id = None
            self.change_state(GC_STOP)
            main = self.main_area
            for child in main.get_children():
                main.remove(child)
                child.destroy()
            self.change_state(GC_INIT)
            self.on_start()

        elif self.status != GC_RECORDING:
            logger.debug("Won't recover from this status")

        else:
            logger.error(
                "Profile changed on the middle of a recording (or something)")

    def on_quit(self, button=None):
        """Close active preview or recoridng and destroys the UI"""
        gui = gtk.Builder()
        gui.add_from_file(get_ui_path("quit.glade"))
        dialog = gui.get_object("dialog")
        dialog.set_transient_for(self.get_toplevel())

        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            dialog.destroy()
            if self.status >= GC_PREVIEW:
                self.record.stop_preview()

            self.change_state(GC_EXIT)
            logger.info("Closing Clock and Scheduler")

            self.scheduler_thread_id = None
            self.clock_thread = None
            # threads closed
            self.emit("delete_event", gtk.gdk.Event(gtk.gdk.DELETE))
        else:
            dialog.destroy()
        return True

    # ------------------------- THREADS ------------------------------

    def timer_launch_thread(self):
        """Thread handling the recording elapsed time timer."""

        # Based on: http://pygstdocs.berlios.de/pygst-tutorial/seeking.html

        thread_id = self.timer_thread_id
        self.initial_time = self.clock.get_time()
        self.initial_datetime = datetime.datetime.utcnow().replace(
            microsecond=0)
        gtk.gdk.threads_enter()
        self.statusbar.SetTimer(0)
        gtk.gdk.threads_leave()

        rec_title = self.gui.get_object("recording1")
        rec_elapsed = self.gui.get_object("recording3")

        while thread_id == self.timer_thread_id:
            #while True:
            actual_time = self.clock.get_time()
            timer = (actual_time - self.initial_time) / gst.SECOND
            dif = datetime.datetime.utcnow() - self.initial_datetime

            if thread_id == self.timer_thread_id:
                gtk.gdk.threads_enter()
                self.statusbar.SetTimer(timer)
                if rec_title.get_text() != self.mediapackage.title:
                    rec_title.set_text(self.mediapackage.title)
                rec_elapsed.set_text("Elapsed Time: " +
                                     self.time_readable(dif))
                gtk.gdk.threads_leave()
            time.sleep(0.2)
        return True

    def scheduler_launch_thread(self):
        """Thread handling the messages scheduler notification area."""
        # Based on: http://pygstdocs.berlios.de/pygst-tutorial/seeking.html
        thread_id = self.scheduler_thread_id
        event_type = self.gui.get_object("nextlabel")
        title = self.gui.get_object("titlelabel")
        status = self.gui.get_object("eventlabel")

        # Status panel
        # status_disk = self.gui.get_object("status1")
        # status_hours = self.gui.get_object("status2")
        # status_mh = self.gui.get_object("status3")

        self.check_schedule()
        parpadeo = True
        changed = False
        signalized = False

        if self.font == None:
            anchura = self.get_toplevel().get_screen().get_width()
            if anchura not in [1024, 1280, 1920]:
                anchura = 1920
            k1 = anchura / 1920.0
            changing_font = pango.FontDescription("bold " + str(k1 * 42))
            self.font = changing_font

        bold = pango.AttrWeight(700, 0, -1)
        red = pango.AttrForeground(65535, 0, 0, 0, -1)
        black = pango.AttrForeground(17753, 17753, 17753, 0, -1)
        font = pango.AttrFontDesc(self.font, 0, -1)

        attr_red = pango.AttrList()
        attr_black = pango.AttrList()

        attr_red.insert(red)
        attr_red.insert(font)
        attr_red.insert(bold)

        attr_black.insert(black)
        attr_black.insert(font)
        attr_black.insert(bold)

        status.set_attributes(attr_black)
        one_second = datetime.timedelta(seconds=1)
        while thread_id == self.scheduler_thread_id:
            if self.font != changing_font:
                attr_black.insert(pango.AttrFontDesc(self.font, 0, -1))
                attr_red.insert(pango.AttrFontDesc(self.font, 0, -1))
                changing_font = self.font
            if self.current:
                start = self.current.getLocalDate()
                duration = self.current.getDuration() / 1000
                end = start + datetime.timedelta(seconds=duration)
                dif = end - datetime.datetime.now()
                #dif2 = datetime.datetime.now() - start
                if dif < datetime.timedelta(0, 0):  # Checking for malfuntions
                    self.current = None
                    self.current_mediapackage = None
                    status.set_text("")
                else:
                    status.set_text("Stopping on " +
                                    self.time_readable(dif + one_second))
                    if event_type.get_text() != CURRENT_TEXT:
                        event_type.set_text(CURRENT_TEXT)
                    if title.get_text() != self.current.title:
                        title.set_text(self.current.title)

                    if dif < datetime.timedelta(0, TIME_RED_STOP):
                        if not changed:
                            status.set_attributes(attr_red)
                            changed = True
                    elif changed:
                        status.set_attributes(attr_black)
                        changed = False
                    if dif < datetime.timedelta(0, TIME_BLINK_STOP):
                        parpadeo = False if parpadeo else True
                # Timer(diff,self.check_schedule)

            elif self.next:
                start = self.next.getLocalDate()
                dif = start - datetime.datetime.now()
                if event_type.get_text != NEXT_TEXT:
                    event_type.set_text(NEXT_TEXT)
                if title.get_text() != self.next.title:
                    title.set_text(self.next.title)
                status.set_text("Starting on " + self.time_readable(dif))

                if dif < datetime.timedelta(0, TIME_RED_START):
                    if not changed:
                        status.set_attributes(attr_red)
                        changed = True
                elif changed:
                    status.set_attributes(attr_black)
                    changed = False

                if dif < datetime.timedelta(0, TIME_UPCOMING):
                    if not signalized:
                        self.dispatcher.emit("upcoming-recording")
                        signalized = True
                elif signalized:
                    signalized = True

                if dif < datetime.timedelta(0, TIME_BLINK_START):
                    if parpadeo:
                        status.set_text("")
                        parpadeo = False
                    else:
                        parpadeo = True
                # Timer(60,self.check_schedule)

            else:  # Not current or pending recordings
                if event_type.get_text():
                    event_type.set_text("")
                if status.get_text():
                    status.set_text("")
                if title.get_text() != "No upcoming events":
                    title.set_text("No upcoming events")

            time.sleep(0.5)
            self.check_schedule()
        return True

    def clock_launch_thread(self):
        """
        Based on: http://pygstdocs.berlios.de/pygst-tutorial/seeking.html
        """
        thread_id = self.clock_thread_id
        clock = self.gui.get_object("local_clock")

        while thread_id == self.clock_thread_id:
            if thread_id == self.clock_thread_id:
                clocktime = datetime.datetime.now().time().strftime("%H:%M")
                clock.set_label(clocktime)
            time.sleep(1)
        return True

    def time_readable(self, timedif):
        """
        Take a timedelta and return it formatted
        """

        if timedif < datetime.timedelta(0, 300):  # 5 minutes tops
            formatted = "{minutes:02d}:{seconds:02d}".format(
                minutes=timedif.seconds // 60, seconds=timedif.seconds % 60)
        elif timedif < datetime.timedelta(1, 0):  # 24 hours
            formatted = "{hours:02d}:{minutes:02d}:{seconds:02d}".format(
                hours=timedif.days * 24 + timedif.seconds // 3600,
                minutes=timedif.seconds % 3600 // 60,
                seconds=timedif.seconds % 60)
        else:  # days
            today = datetime.datetime.now()
            then = today + timedif
            dif = then.date() - today.date()
            formatted = "{days} day{plural}".format(
                days=dif.days, plural='s' if dif.days > 1 else '')

        return formatted

    def check_schedule(self):
        # previous1 = self. current
        previous2 = self.next
        if self.current_mediapackage == None:
            self.current = None
        else:
            self.current = self.repo.get(self.current_mediapackage)
        previous2 = self.next
        self.next = self.repo.get_next_mediapackage()  # could be None
        if previous2 != self.next:
            self.reload_state()

    # ------------------------- POPUP ACTIONS ------------------------

    def on_edit_meta(self, button):
        """Pops up the  Metadata editor of the active Mediapackage"""
        #self.change_state(GC_BLOCKED)
        if not self.scheduled_recording:
            Metadata(self.mediapackage, parent=self)
            self.statusbar.SetVideo(
                None, self.mediapackage.metadata_episode['title'])
            self.statusbar.SetPresenter(None, self.mediapackage.creators)
        #self.change_state(self.previous)
        return True

    def show_next(self, button=None, tipe=None):
        """Pops up the Event Manager"""
        EventManager()
        return True

    def show_about(self, button=None, tipe=None):
        """Pops up de About Dialgo"""
        GCAboutDialog()

    # -------------------------- UI ACTIONS -----------------------------

    def create_drawing_areas(self, source):
        """Create as preview areas as video sources exits"""
        main = self.main_area

        for child in main.get_children():
            main.remove(child)
            child.destroy()
        areas = None
        areas = dict()
        for key, value in source.iteritems():
            new_area = gtk.DrawingArea()
            new_area.set_name("videoarea" + str(key))
            new_area.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("black"))
            areas[re.sub(r'\W+', '', value)] = new_area
            main.pack_start(new_area, True, True, int(self.proportion * 3))

        for child in main.get_children():
            child.show()

        return areas

    def event_change_mode(self, orig, old_state, new_state):
        """Handles the focus or the Rercording Area, launching messages when focus is recoverde"""
        if new_state == 0:
            self.focus_is_active = True
            if self.record:
                self.record.mute_preview(False)
            if self.error_text:
                if self.status != GC_ERROR:
                    self.change_state(GC_ERROR)
                self.launch_error_message(self.error_text)

        if old_state == 0:
            self.focus_is_active = False
            if self.record:
                self.record.mute_preview(True)

    def change_mode(self, button):
        """Launch the signal to change to another area"""
        self.dispatcher.emit("change-mode", 3)  # FIXME use constant

    def set_status_view(self):
        """Set the message and color of the status pilot on the top bar"""

        size = context.get_mainwindow().get_size()
        # k1 = size[0] / 1920.0
        k2 = size[1] / 1080.0

        l = gtk.ListStore(str, str, str)

        main_window = context.get_mainwindow()
        main_window.realize()
        style = main_window.get_style()

        bgcolor = style.bg[gtk.STATE_PRELIGHT]
        fgcolor = style.fg[gtk.STATE_PRELIGHT]

        for i in STATUS:
            if i[0] in ["Recording", "Error"]:
                l.append([i[0], i[1], fgcolor])
            else:
                l.append([i[0], bgcolor, fgcolor])

        v = gtk.CellView()
        v.set_model(l)

        r = gtk.CellRendererText()
        self.renderer = r
        r.set_alignment(0.5, 0.5)
        r.set_fixed_size(int(k2 * 400), -1)

        # k1 = size[0] / 1920.0
        k2 = size[1] / 1080.0
        font = pango.FontDescription("bold " + str(int(k2 * 48)))
        r.set_property('font-desc', font)
        v.pack_start(r, True)
        v.add_attribute(r, "text", 0)
        v.add_attribute(r, "background", 1)
        v.add_attribute(r, "foreground", 2)
        v.set_displayed_row(0)
        return v

    def check_status_area(self, origin, signal=None, other=None):
        """Updates the values on the recording tab"""
        s1 = self.gui.get_object("status1")
        s2 = self.gui.get_object("status2")
        # s3 = self.gui.get_object("status3")
        s4 = self.gui.get_object("status4")

        freespace, text_space = status_bar.GetFreeSpace(
            self.repo.get_attach_path())
        s1.set_text(text_space)
        four_gb = 4000000000.0
        hours = int(freespace / four_gb)
        s2.set_text(str(hours) + " hours left")
        agent = context.get_state().hostname  # TODO just consult it once
        if s4.get_text() != agent:
            s4.set_text(agent)

    def check_net(self, origin, status=None):
        """Update the value of the network status"""
        attr1 = pango.AttrList()
        attr2 = pango.AttrList()
        attr3 = pango.AttrList()
        attr4 = pango.AttrList()
        gray = pango.AttrForeground(20000, 20000, 20000, 0, -1)
        red = pango.AttrForeground(65535, 0, 0, 0, -1)
        green = pango.AttrForeground(0, 65535, 0, 0, -1)
        black = pango.AttrForeground(5000, 5000, 5000, 0, -1)
        attr1.insert(gray)
        attr2.insert(green)
        attr3.insert(red)
        attr4.insert(black)

        s3 = self.gui.get_object("status3")
        if not self.net_activity:
            s3.set_text("Disabled")
            s3.set_attributes(attr1)
        else:
            net = status or context.get_state().net
            try:
                if net:
                    s3.set_text("Up")
                    s3.set_attributes(attr2)
                else:
                    s3.set_text("Down")
                    s3.set_attributes(attr3)
            except KeyError:
                s3.set_text("Connecting")
                s3.set_attributes(attr4)

    def resize(self):
        """Adapts GUI elements to the screen size"""
        size = context.get_mainwindow().get_size()
        altura = size[1]
        anchura = size[0]

        k1 = anchura / 1920.0
        k2 = altura / 1080.0
        self.proportion = k1

        #Recorder
        clock = self.gui.get_object("local_clock")
        logo = self.gui.get_object("classlogo")
        nextl = self.gui.get_object("nextlabel")
        title = self.gui.get_object("titlelabel")
        # eventl = self.gui.get_object("eventlabel")
        pbox = self.gui.get_object("prebox")

        rec_title = self.gui.get_object("recording1")
        rec_elapsed = self.gui.get_object("recording3")
        status_panel = self.gui.get_object('status_panel')

        l1 = self.gui.get_object("tab1")
        l2 = self.gui.get_object("tab2")
        l3 = self.gui.get_object("tab3")

        relabel(clock, k1 * 25, False)
        font = pango.FontDescription("bold " + str(int(k2 * 48)))
        self.renderer.set_property('font-desc', font)
        self.renderer.set_fixed_size(int(k2 * 400), -1)
        pixbuf = gtk.gdk.pixbuf_new_from_file(get_image_path('logo.svg'))
        pixbuf = pixbuf.scale_simple(int(pixbuf.get_width() * k1),
                                     int(pixbuf.get_height() * k1),
                                     gtk.gdk.INTERP_BILINEAR)
        logo.set_from_pixbuf(pixbuf)

        modification = "bold " + str(k1 * 42)
        self.font = pango.FontDescription(modification)
        relabel(nextl, k1 * 25, True)
        relabel(title, k1 * 33, True)

        # REC AND STATUS PANEL
        relabel(rec_title, k1 * 25, True)
        rec_title.set_line_wrap(True)
        rec_title.set_width_chars(40)
        relabel(rec_elapsed, k1 * 28, True)

        for child in status_panel.get_children():
            if type(child) is gtk.Label:
                relabel(child, k1 * 19, True)
        relabel(l1, k1 * 20, False)
        relabel(l2, k1 * 20, False)
        relabel(l3, k1 * 20, False)

        for name in ["recbutton", "pausebutton", "stopbutton", "helpbutton"]:
            button = self.gui.get_object(name)
            button.set_property("width-request", int(k1 * 100))
            button.set_property("height-request", int(k1 * 100))

            image = button.get_children()
            if type(image[0]) == gtk.Image:
                image[0].set_pixel_size(int(k1 * 80))
            elif type(image[0]) == gtk.VBox:
                for element in image[0].get_children():
                    if type(element) == gtk.Image:
                        element.set_pixel_size(int(k1 * 46))
            else:
                relabel(image[0], k1 * 28, False)

        for name in ["previousbutton", "morebutton"]:
            button = self.gui.get_object(name)
            button.set_property("width-request", int(k1 * 70))
            button.set_property("height-request", int(k1 * 70))

            image = button.get_children()
            if type(image[0]) == gtk.Image:
                image[0].set_pixel_size(int(k1 * 56))

        talign = self.gui.get_object("top_align")
        talign.set_padding(int(k1 * 10), int(k1 * 25), 0, 0)
        calign = self.gui.get_object("control_align")
        calign.set_padding(int(k1 * 10), int(k1 * 30), int(k1 * 50),
                           int(k1 * 50))
        vum = self.gui.get_object("vubox")
        vum.set_padding(int(k1 * 20), int(k1 * 10), int(k1 * 40), int(k1 * 40))
        pbox.set_property("width-request", int(k1 * 225))
        return True

    def change_state(self, state):
        """Activates or deactivates the buttons depending on the new state"""
        record = self.gui.get_object("recbutton")
        pause = self.gui.get_object("pausebutton")
        stop = self.gui.get_object("stopbutton")
        helpb = self.gui.get_object("helpbutton")
        editb = self.gui.get_object("editbutton")
        prevb = self.gui.get_object("previousbutton")

        if state != self.status:
            self.previous, self.status = self.status, state

        if state == GC_INIT:
            record.set_sensitive(False)
            pause.set_sensitive(False)
            stop.set_sensitive(False)
            helpb.set_sensitive(True)
            prevb.set_sensitive(True)
            editb.set_sensitive(False)
            self.dispatcher.emit("update-rec-status", "Initialization")

        elif state == GC_PREVIEW:
            record.set_sensitive((self.allow_start or self.allow_manual))
            pause.set_sensitive(False)
            pause.set_active(False)
            stop.set_sensitive(False)
            helpb.set_sensitive(True)
            prevb.set_sensitive(True)
            editb.set_sensitive(False)
            if self.next == None:
                self.dispatcher.emit("update-rec-status", "Idle")
            else:
                self.dispatcher.emit("update-rec-status", "Waiting")

        elif state == GC_RECORDING:
            record.set_sensitive(False)
            pause.set_sensitive(self.allow_pause and self.record.is_pausable())
            stop.set_sensitive((self.allow_stop or self.allow_manual))
            helpb.set_sensitive(True)
            prevb.set_sensitive(False)
            editb.set_sensitive(True and not self.scheduled_recording)
            self.dispatcher.emit("update-rec-status", "  Recording  ")

        elif state == GC_PAUSED:
            record.set_sensitive(False)
            pause.set_sensitive(False)
            stop.set_sensitive(False)
            prevb.set_sensitive(False)
            helpb.set_sensitive(False)
            editb.set_sensitive(False)

            self.dispatcher.emit("update-rec-status", "Paused")

        elif state == GC_STOP:
            if self.previous == GC_PAUSED:
                self.pause_dialog.destroy()
            record.set_sensitive(False)
            pause.set_sensitive(False)
            stop.set_sensitive(False)
            helpb.set_sensitive(True)
            prevb.set_sensitive(True)
            editb.set_sensitive(False)
            self.dispatcher.emit("update-rec-status", "Stopped")

        elif state == GC_BLOCKED:
            record.set_sensitive(False)
            pause.set_sensitive(False)
            stop.set_sensitive(False)
            helpb.set_sensitive(False)
            prevb.set_sensitive(False)
            editb.set_sensitive(False)

        elif state == GC_ERROR:
            record.set_sensitive(False)
            pause.set_sensitive(False)
            stop.set_sensitive(False)
            helpb.set_sensitive(True)
            prevb.set_sensitive(True)
            editb.set_sensitive(False)

        if self.next == None and state == GC_PREVIEW:
            self.view.set_displayed_row(GC_PRE2)
        else:
            self.view.set_displayed_row(state)

    def block(self):
        prev = self.gui.get_object("prebox")
        prev.set_child_visible(False)
        self.focus_is_active = True
        self.event_change_mode(None, 3, 0)

        # Show Help or Edit_meta
        helpbutton = self.gui.get_object("helpbutton")
        helpbutton.set_visible(True)
        editbutton = self.gui.get_object("editbutton")
        editbutton.set_visible(False)

        # Start Preview
        self.dispatcher.emit("start-preview")

    def close(self, signal):
        """Handles the area closure, stopping threads, mediapackage and preview"""
        if self.status in [GC_RECORDING]:
            self.close_recording()
        self.scheduler_thread_id = None
        self.clock_thread_id = None
        self.start_thread_id = None
        if self.status in [GC_PREVIEW]:
            self.record.stop_preview()
        return True
Example #5
0
class PlayerClassUI(ManagerUI):
    """
    Graphic User Interface for Listing Player and Player Alone
    """
    __gtype_name__ = 'PlayerClass'
    
    
    def __init__(self,package=None):       
        ManagerUI.__init__(self,1)
	builder = gtk.Builder()
        builder.add_from_file(get_ui_path('player.glade'))
        self.gui=builder

        # BUILD GUI
        self.playerui = builder.get_object("playerbox")
        self.main_area = builder.get_object("videobox")
        self.player = None

        # Seek Bar
        self.duration = 0
        self.seeking = False
        self.jump=0 # seek value
        self.jump_id=0 # seek signal id
        self.correct=False # To correct SCROLL_JUMP, after release it
        self.seek_bar=self.gui.get_object("seekbar")
        self.seek_bar.add_events(gtk.gdk.SCROLL_MASK)
        self.seek_bar.connect("change-value", self.on_seek) 
      
        # VUMETER
        self.audiobar=AudioBarClass(True)
        self.volume_bar = self.audiobar.volume
        self.volume_bar.connect("value-changed", self.on_volume2) 

        self.vubox = builder.get_object("vubox")
        self.vubox.add(self.audiobar.bar)

        # STATUSBAR
        self.statusbar= StatusBarClass()
        sbox = builder.get_object("statusbox")
        sbox.add(self.statusbar.bar)
      
        self.playerui.pack_start(self.strip,False,False,0)
        self.playerui.reorder_child(self.strip,0)
        self.pack_start(self.playerui,True,True,0)

        self.status=GC_INIT
        self.previous=None
        self.change_state(GC_INIT)
        self.mediapackage=None # The Mediapackage being reproduced

        self.thread_id=None
        builder.connect_signals(self)

        self.dispatcher.connect("update-play-vumeter", self.audiobar.SetVumeter)
        self.dispatcher.connect("play-stopped", self.change_state_bypass, GC_READY)
        self.dispatcher.connect("galicaster-status", self.event_change_mode)
        self.dispatcher.connect("galicaster-notify-quit", self.close)


#-------------------------- INIT PLAYER-----------------------
    def init_player(self,element,mp):
        """Send absolute file names and Drawing Areas to the player."""
        self.mediapackage = mp

        tracks = OrderedDict()
        videos = OrderedDict()

        index = 0
    
        for t in mp.getTracks(): 
            if not t.getFlavor().count('other'):
                tracks[t.getIdentifier()] = t.getURI()

            if (t.getMimeType().count("video") and not t.getFlavor().count('other')):
                index+=1
                videos[t.getIdentifier()] = index


        areas = self.create_drawing_areas(videos)
        
        self.seek_bar.set_value(0)
        if self.player:
            self.player.quit()
                  
        self.player = Player(tracks, areas)
        self.change_state(GC_READY)

        self.statusbar.SetVideo(None, self.mediapackage.title)
        self.statusbar.SetPresenter(None, self.mediapackage.getCreators())
        #self.dispatcher.emit

    def play_from_list(self,package):
        """Takes a MP from the listing area and plays it"""
        if self.status == GC_PAUSE:
            self.on_stop_clicked()
            self.statusbar.ClearTimer()            

        self.ready_id = self.dispatcher.connect('player-ready',self.on_player_ready)     
        self.init_player(None, package)          

    def on_player_ready(self, origin):
        """Auto-starts the player once ready"""
        self.dispatcher.disconnect(self.ready_id)
        self.init_timer()
        return True
        

#------------------------- PLAYER ACTIONS ------------------------

    def on_play_clicked(self, button):
        """Starts the reproduction"""
        self.player.play()
        self.init_timer()
        return True

    def init_timer(self):
        """Starts the thread handling the timer for visualiztion and seeking feature"""
        self.change_state(GC_PLAY)
        self.timer_thread = threading.Thread(target=self.timer_launch_thread)
        self.thread_id = 1
        self.timer_thread.daemon = True
        self.timer_thread.start()
        
    def on_pause_clicked(self, button=None):
        """Pauses the reproduction"""
        self.player.pause()
        self.change_state(GC_PAUSE)
        return True

    def on_stop_clicked(self, button=None):
        """Stops the reproduction and send the seek bar to the start"""
        self.thread_id = None
        self.player.stop()
        self.seek_bar.set_value(0)
        self.statusbar.SetTimer2(0,self.duration)
        self.change_state(GC_STOP)
        return True

    def on_quit_clicked(self, button):
        """Stops the preview and close the player"""
        gui = gtk.Builder()
        gui.add_from_file(get_ui_path("quit.glade"))
        dialog = gui.get_object("dialog")
        response = dialog.run()
        if response == gtk.RESPONSE_OK:   
            dialog.destroy()
            if self.status > 0:
                self.player.quit()
            self.change_state(GC_EXIT)
            self.emit("delete_event", gtk.gdk.Event(gtk.gdk.DELETE))            
        else:
            dialog.destroy()
        return True

    def focus_out(self, button, event):
        """Stop the player when focus is lost"""
        self.player.pause()
        self.change_state(GC_STOP)

    def on_seek(self, button, scroll_type, new_value):
        """Move to the new position"""
        if new_value>100:
            new_value=100;
        temp=new_value*self.duration*gst.SECOND/100 # FIXME get_duration propertly

        if scroll_type == gtk.SCROLL_JUMP and not self.correct:
            self.seeking = True
            if self.player.is_playing():
                self.player.pause()
            value=new_value * self.duration // 100 
            self.statusbar.SetTimer2(value,self.duration)
            self.jump=temp
            if not self.jump_id:
                log.warning("Handling Seek Jump")
                self.jump_id=self.seek_bar.connect("button-release-event",self.on_seek,0)# FIXME ensure real release, not click                        
        if self.correct:
            self.correct=False            

        if scroll_type != gtk.SCROLL_JUMP: # handel regular scroll
            if scroll_type ==  gtk.SCROLL_PAGE_FORWARD or scroll_type ==  gtk.SCROLL_PAGE_BACKWARD:
                self.player.seek(temp, False)

            else: # handle jump
                self.player.seek(self.jump, True) # jump to the position where the cursor was released
                self.seek_bar.disconnect(self.jump_id)
                self.jump_id=0 # jump finished and disconnected
                self.correct=True # correction rutine activated
                self.seeking= False

    def on_volume(self, button, scroll_type, new_value):
        """Changes the player value"""
        value = 120 if new_value > 120 else 0 if new_value < 0 else new_value
        self.player.set_volume(value/100.0)

    def on_volume2(self, button, new_value):
        self.player.set_volume(new_value*2.0)

    def create_drawing_areas(self, source): # TODO refactorize, REC
        """Creates the preview areas depending on the video tracks of a mediapackage"""
        main = self.main_area

        for child in main.get_children():
            main.remove(child)
            child.destroy()        
        areas = None
        areas = dict()
        for key in source.keys():
            new_area = gtk.DrawingArea()
            new_area.set_name("playerarea "+str(key))
            areas[key]=new_area
            areas[key].modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("black"))
            main.pack_start(new_area,True,True,3)#TODO editable padding=3

        for child in main.get_children():
            child.show()
         
        return areas
        

#------------------------- PACKAGE ACTIONS ------------------------

    def on_edit_meta(self,button):
        """Pop ups the Medatada Editor for the current Mediapackage"""
        key = self.mediapackage.identifier
        self.edit(key)
        self.statusbar.SetVideo(None, self.mediapackage.title)
        self.statusbar.SetPresenter(None, self.mediapackage.getCreators())
        return True

    def on_question(self,button):
        """Pops up a dialog with the available operations"""
        package = self.mediapackage
        self.ingest_question(package)      

    def on_delete(self, button):
        """ Pops up a dialog.
        If response is positive the mediapackage is deleted and the focus goes to the previous area"""
        key = self.mediapackage.identifier
        response=self.delete(key)		
        if response:
            self.thread_id = None
            self.player.stop()
            self.statusbar.SetVideo(None, "")
            self.statusbar.SetPresenter(None, "")
            self.statusbar.ClearTimer()
            self.change_state(GC_INIT)
            self.mediapackage = None
            self.dispatcher.emit("change-mode", 1)
            
        return True
    
#-------------------------- UI ACTIONS -----------------------------

    def timer_launch_thread(self):
        """Thread handling the timer, provides base time for seeking"""
        thread_id= self.thread_id
        self.initial_time=self.player.get_time()
        self.duration = self.player.get_duration()
        gtk.gdk.threads_enter()
        self.statusbar.SetTimer2(0,self.duration)
        gtk.gdk.threads_leave()        
              
        self.volume_bar.set_value(0.5)

        while thread_id == self.thread_id:
            if not self.seeking :
                if not self.duration:
                    actual_time=self.player.get_time()  
                    timer=(actual_time-self.initial_time)/gst.SECOND
                else:
                    try:
                        actual_time, format_type =self.player.get_position()
                    except:
                            actual_time = 0                        
                            log.warning("Query position failed")

                    timer = actual_time / gst.SECOND
                    self.seek_bar.set_value(timer*100/self.duration)
                if thread_id==self.thread_id:
                    gtk.gdk.threads_enter()
                    self.statusbar.SetTimer2(timer,self.duration)
                    gtk.gdk.threads_leave()
                    
            time.sleep(0.2)          
        return True

    def resize(self):
        """Adapts GUI elements to the screen size"""
        size = context.get_mainwindow().get_size()
        
        buttonlist = ["playbutton", "pausebutton", "stopbutton"]
        secondarylist = ["editbutton", "ingestbutton", "deletebutton"]
        self.do_resize(buttonlist, secondarylist)
        vubox = self.gui.get_object("vubox")
        calign = self.gui.get_object("c_align")

        k = self.proportion
        vubox.set_padding(0,int(k*10),int(k*20),int(k*40))
        calign.set_padding(int(k*20),int(k*10),0,0)
  
        self.statusbar.resize(size)
        self.audiobar.resize(size)
        return True

    def event_change_mode(self, orig, old_state, new_state):
        """Pause a recording in the event of a change of mode"""
        if old_state == 2 and self.status == GC_PLAY:
            self.on_pause_clicked()        

    def change_state_bypass(self,origin,state):
        """To handles change state through signal"""
        self.change_state(state)
        return True

    def change_state(self,state):
        """Activates or deactivates the buttons depending on the new state"""
        play=self.gui.get_object("playbutton")
        pause=self.gui.get_object("pausebutton")
        stop=self.gui.get_object("stopbutton")
        editb=self.gui.get_object("editbutton")       
        deleteb=self.gui.get_object("deletebutton")       

        self.previous,self.status = self.status,state

        if state==GC_INIT:
            play.set_sensitive(False)
            pause.set_sensitive(False)
            stop.set_sensitive(False)
            editb.set_sensitive(False)
            deleteb.set_sensitive(False)

        if state==GC_READY:
            play.set_sensitive(True)
            pause.set_sensitive(False)
            stop.set_sensitive(False)
            editb.set_sensitive(True)
            deleteb.set_sensitive(True)

        if state==GC_PLAY:
            play.set_sensitive(False)
            pause.set_sensitive(True)
            stop.set_sensitive(True)

        if state==GC_PAUSE:
            play.set_sensitive(True)
            pause.set_sensitive(False)
            stop.set_sensitive(True)
            
        if state==GC_STOP:
            play.set_sensitive(True)
            pause.set_sensitive(False)
            stop.set_sensitive(False)

        if state==GC_BLOCKED:
            play.set_sensitive(False)
            pause.set_sensitive(False)
            stop.set_sensitive(False)

    def close(self, signal):
        """Close player UI, stopping threads and reproduction"""
        self.thread_id=None
        if self.status in [GC_PLAY, GC_PAUSE]:
            self.player.quit()
        return True        
Example #6
0
class PlayerClassUI(ManagerUI):
    """
    Graphic User Interface for Listing Player and Player Alone
    """
    __gtype_name__ = 'PlayerClass'

    def __init__(self, package=None):
        ManagerUI.__init__(self, 1)
        builder = gtk.Builder()
        builder.add_from_file(get_ui_path('player.glade'))
        self.gui = builder

        # BUILD GUI
        self.playerui = builder.get_object("playerbox")
        self.main_area = builder.get_object("videobox")
        self.player = None

        # Seek Bar
        self.duration = 0
        self.seeking = False
        self.jump = 0  # seek value
        self.jump_id = 0  # seek signal id
        self.correct = False  # To correct SCROLL_JUMP, after release it
        self.seek_bar = self.gui.get_object("seekbar")
        self.seek_bar.add_events(gtk.gdk.SCROLL_MASK)
        self.seek_bar.connect("change-value", self.on_seek)

        # VUMETER
        self.audiobar = AudioBarClass()
        self.volume_bar = self.audiobar.volume
        self.volume_bar.connect("value-changed", self.on_volume2)

        self.vubox = builder.get_object("vubox")
        self.vubox.add(self.audiobar.bar)

        # STATUSBAR
        self.statusbar = StatusBarClass()
        sbox = builder.get_object("statusbox")
        sbox.add(self.statusbar.bar)

        self.playerui.pack_start(self.strip, False, False, 0)
        self.playerui.reorder_child(self.strip, 0)
        self.pack_start(self.playerui, True, True, 0)

        self.status = GC_INIT
        self.previous = None
        self.change_state(GC_INIT)
        self.mediapackage = None  # The Mediapackage being reproduced

        self.thread_id = None
        builder.connect_signals(self)

        self.dispatcher.connect("update-play-vumeter",
                                self.audiobar.SetVumeter)
        self.dispatcher.connect("play-stopped", self.change_state_bypass,
                                GC_READY)
        self.dispatcher.connect('play-list', self.play_from_list)
        self.dispatcher.connect("galicaster-status", self.event_change_mode)
        self.dispatcher.connect("galicaster-notify-quit", self.close)

#-------------------------- INIT PLAYER-----------------------

    def init_player(self, element, mp):
        """Send absolute file names and Drawing Areas to the player."""
        if (self.mediapackage != mp):
            if self.status == GC_PAUSE:
                self.on_stop_clicked()
                self.statusbar.ClearTimer()

            self.mediapackage = mp

            tracks = OrderedDict()
            videos = OrderedDict()

            index = 0

            for t in mp.getTracks():
                if not t.getFlavor().count('other'):
                    tracks[t.getIdentifier()] = t.getURI()

                    if (t.getMimeType().count("video")
                            and not t.getFlavor().count('other')):
                        index += 1
                        videos[t.getIdentifier()] = index

            areas = self.create_drawing_areas(videos)

            self.seek_bar.set_value(0)
            if self.player:
                self.player.quit()

            self.player = Player(tracks, areas)
            self.change_state(GC_READY)

            self.statusbar.SetVideo(None, self.mediapackage.title)
            self.statusbar.SetPresenter(None, self.mediapackage.getCreator())

        self.on_play_clicked(None)

    def play_from_list(self, origin, package):
        """Takes a MP from the listing area and plays it"""
        self.dispatcher.emit("change-mode", 2)
        self.init_player(None, package)

#------------------------- PLAYER ACTIONS ------------------------

    def on_play_clicked(self, button):
        """Starts the reproduction"""
        self.change_state(GC_PLAY)
        self.player.play()
        self.init_timer()
        return True

    def init_timer(self):
        """Starts the thread handling the timer for visualiztion and seeking feature"""
        self.timer_thread = threading.Thread(target=self.timer_launch_thread)
        self.thread_id = 1
        self.timer_thread.daemon = True
        self.timer_thread.start()

    def on_pause_clicked(self, button=None):
        """Pauses the reproduction"""
        self.player.pause()
        self.change_state(GC_PAUSE)
        return True

    def on_stop_clicked(self, button=None):
        """Stops the reproduction and send the seek bar to the start"""
        self.thread_id = None
        self.player.stop()
        self.seek_bar.set_value(0)
        self.statusbar.SetTimer2(0, self.duration)
        self.change_state(GC_STOP)
        return True

    def on_quit_clicked(self, button):
        """Stops the preview and close the player"""
        gui = gtk.Builder()
        gui.add_from_file(get_ui_path("quit.glade"))
        dialog = gui.get_object("dialog")
        response = dialog.run()
        if response == gtk.RESPONSE_OK:
            dialog.destroy()
            if self.status > 0:
                self.player.quit()
            self.change_state(GC_EXIT)
            self.emit("delete_event", gtk.gdk.Event(gtk.gdk.DELETE))
        else:
            dialog.destroy()
        return True

    def focus_out(self, button, event):
        """Stop the player when focus is lost"""
        self.player.pause()
        self.change_state(GC_STOP)

    def on_seek(self, button, scroll_type, new_value):
        """Move to the new position"""
        if new_value > 100:
            new_value = 100
        temp = new_value * self.duration * gst.SECOND / 100  # FIXME get_duration propertly

        if scroll_type == gtk.SCROLL_JUMP and not self.correct:
            self.seeking = True
            if self.player.is_playing():
                self.player.pause()
            value = new_value * self.duration // 100
            self.statusbar.SetTimer2(value, self.duration)
            self.jump = temp
            if not self.jump_id:
                log.warning("Handling Seek Jump")
                self.jump_id = self.seek_bar.connect(
                    "button-release-event", self.on_seek,
                    0)  # FIXME ensure real release, not click
        if self.correct:
            self.correct = False

        if scroll_type != gtk.SCROLL_JUMP:  # handel regular scroll
            if scroll_type == gtk.SCROLL_PAGE_FORWARD or scroll_type == gtk.SCROLL_PAGE_BACKWARD:
                self.player.seek(temp, False)

            else:  # handle jump
                self.player.seek(
                    self.jump,
                    True)  # jump to the position where the cursor was released
                self.seek_bar.disconnect(self.jump_id)
                self.jump_id = 0  # jump finished and disconnected
                self.correct = True  # correction rutine activated
                self.seeking = False

    def on_volume(self, button, scroll_type, new_value):
        """Changes the player value"""
        value = 120 if new_value > 120 else 0 if new_value < 0 else new_value
        self.player.set_volume(value / 100.0)

    def on_volume2(self, button, new_value):
        self.player.set_volume(new_value * 2.0)

    def create_drawing_areas(self, source):  # TODO refactorize, REC
        """Creates the preview areas depending on the video tracks of a mediapackage"""
        main = self.main_area

        for child in main.get_children():
            main.remove(child)
            child.destroy()
        areas = None
        areas = dict()
        for key in source.keys():
            new_area = gtk.DrawingArea()
            new_area.set_name("playerarea " + str(key))
            areas[key] = new_area
            areas[key].modify_bg(gtk.STATE_NORMAL,
                                 gtk.gdk.color_parse("black"))
            main.pack_start(new_area, True, True, 3)  #TODO editable padding=3

        for child in main.get_children():
            child.show()

        return areas

#------------------------- PACKAGE ACTIONS ------------------------

    def on_edit_meta(self, button):
        """Pop ups the Medatada Editor for the current Mediapackage"""
        key = self.mediapackage.identifier
        self.edit(key)
        self.statusbar.SetVideo(None, self.mediapackage.title)
        self.statusbar.SetPresenter(None, self.mediapackage.getCreator())
        return True

    def on_question(self, button):
        """Pops up a dialog with the available operations"""
        package = self.mediapackage
        self.ingest_question(package)

    def on_delete(self, button):
        """ Pops up a dialog.
        If response is positive the mediapackage is deleted and the focus goes to the previous area"""
        key = self.mediapackage.identifier
        response = self.delete(key)
        if response:
            self.thread_id = None
            self.player.stop()
            self.statusbar.SetVideo(None, "")
            self.statusbar.SetPresenter(None, "")
            self.statusbar.ClearTimer()
            self.change_state(GC_INIT)
            self.mediapackage = None
            self.dispatcher.emit("change-mode", 1)

        return True


#-------------------------- UI ACTIONS -----------------------------

    def timer_launch_thread(self):
        """Thread handling the timer, provides base time for seeking"""
        thread_id = self.thread_id
        self.initial_time = self.player.get_time()
        self.duration = self.player.get_duration()
        gtk.gdk.threads_enter()
        self.statusbar.SetTimer2(0, self.duration)
        gtk.gdk.threads_leave()

        self.volume_bar.set_value(0.5)

        while thread_id == self.thread_id:
            if not self.seeking:
                if not self.duration:
                    actual_time = self.player.get_time()
                    timer = (actual_time - self.initial_time) / gst.SECOND
                else:
                    try:
                        actual_time, format_type = self.player.get_position()
                    except:
                        actual_time = 0
                        log.warning("Query position failed")

                    timer = actual_time / gst.SECOND
                    self.seek_bar.set_value(timer * 100 / self.duration)
                if thread_id == self.thread_id:
                    gtk.gdk.threads_enter()
                    self.statusbar.SetTimer2(timer, self.duration)
                    gtk.gdk.threads_leave()

            time.sleep(0.2)
        return True

    def resize(self):
        """Adapts GUI elements to the screen size"""
        size = context.get_mainwindow().get_size()

        buttonlist = ["playbutton", "pausebutton", "stopbutton"]
        secondarylist = ["editbutton", "ingestbutton", "deletebutton"]
        self.do_resize(buttonlist, secondarylist)
        vubox = self.gui.get_object("vubox")
        calign = self.gui.get_object("c_align")

        k = self.proportion
        vubox.set_padding(0, int(k * 10), int(k * 20), int(k * 40))
        calign.set_padding(int(k * 20), int(k * 10), 0, 0)

        self.statusbar.resize(size)
        self.audiobar.resize(size)
        return True

    def event_change_mode(self, orig, old_state, new_state):
        """Pause a recording in the event of a change of mode"""
        if old_state == 2 and self.status == GC_PLAY:
            self.on_pause_clicked()

    def change_state_bypass(self, origin, state):
        """To handles change state through signal"""
        self.change_state(state)
        return True

    def change_state(self, state):
        """Activates or deactivates the buttons depending on the new state"""
        play = self.gui.get_object("playbutton")
        pause = self.gui.get_object("pausebutton")
        stop = self.gui.get_object("stopbutton")
        editb = self.gui.get_object("editbutton")
        deleteb = self.gui.get_object("deletebutton")

        self.previous, self.status = self.status, state

        if state == GC_INIT:
            play.set_sensitive(False)
            pause.set_sensitive(False)
            stop.set_sensitive(False)
            editb.set_sensitive(False)
            deleteb.set_sensitive(False)

        if state == GC_READY:
            play.set_sensitive(True)
            pause.set_sensitive(False)
            stop.set_sensitive(False)
            editb.set_sensitive(True)
            deleteb.set_sensitive(True)

        if state == GC_PLAY:
            play.set_sensitive(False)
            pause.set_sensitive(True)
            stop.set_sensitive(True)

        if state == GC_PAUSE:
            play.set_sensitive(True)
            pause.set_sensitive(False)
            stop.set_sensitive(True)

        if state == GC_STOP:
            play.set_sensitive(True)
            pause.set_sensitive(False)
            stop.set_sensitive(False)

        if state == GC_BLOCKED:
            play.set_sensitive(False)
            pause.set_sensitive(False)
            stop.set_sensitive(False)

    def close(self, signal):
        """Close player UI, stopping threads and reproduction"""
        self.thread_id = None
        if self.status in [GC_PLAY, GC_PAUSE]:
            self.player.quit()
        return True