Esempio n. 1
0
class TVGuide(Item):
    """
    Class for TVGuide
    """
    def __init__(self, start_time, player, menuw):
        _debug_(
            'TVGuide.__init__(start_time=%r, player=%r, menuw=%r)' %
            (start_time, player, menuw), 2)
        Item.__init__(self)

        # get skin definitions of the TVGuide
        self.n_items, self.hours_per_page = skin.items_per_page(('tv', self))
        # end of visible guide
        stop_time = start_time + self.hours_per_page * 60 * 60

        # constructing the guide takes some time
        msgtext = _('Preparing the program guide')
        guide = tv.epg_xmltv.get_guide(PopupBox(text=msgtext))
        # getting channels
        channels = guide.get_programs(start_time + 1, stop_time - 1)
        if not channels:
            AlertBox(text=_('TV Guide is corrupt!')).show()
            return

        # select the first available program
        selected = None
        for chan in channels:
            if chan.programs:
                self.selected = chan.programs[0]
                break

        self.recordclient = RecordClient()
        self.col_time = 30  # each col represents 30 minutes
        self.n_cols = (stop_time - start_time) / 60 / self.col_time
        self.player = player

        self.type = 'tv'
        self.menuw = menuw
        self.visible = True
        self.select_time = start_time
        self.last_update = 0

        self.lastinput_value = None
        self.lastinput_time = None

        self.update_schedules(force=True)

        self.event_context = 'tvmenu'
        self.rebuild(start_time, stop_time, guide.chan_list[0].id, selected)
        menuw.pushmenu(self)

    def update_schedules_cb(self, scheduledRecordings):
        """ """
        _debug_(
            'update_schedules_cb(scheduledRecordings=%r)' %
            (scheduledRecordings, ), 2)
        #upsoon = '%s/upsoon' % (config.FREEVO_CACHEDIR)
        #if os.path.isfile(upsoon):
        #    os.unlink(upsoon)

        util.misc.comingup(None, (True, scheduledRecordings))
        progs = scheduledRecordings.getProgramList()

        for k in progs:
            prog = progs[k]
            self.scheduled_programs.append(prog.str2utf())
            if prog.overlap:
                self.overlap_programs.append(prog.str2utf())
            if hasattr(prog, 'isFavorite') and prog.isFavorite:
                self.favorite_programs.append(prog.str2utf())

    @benchmark(benchmarking)
    def update_schedules(self, force=False):
        """
        update schedule

        reload the list of scheduled programs and check for overlapping
        """
        _debug_('update_schedules(force=%r)' % (force, ), 2)
        if not force and self.last_update + 60 > time.time():
            return

        _debug_('update schedule', 2)
        self.last_update = time.time()
        self.scheduled_programs = []
        self.overlap_programs = []
        self.favorite_programs = []
        self.recordclient.getScheduledRecordings(self.update_schedules_cb)

    @benchmark(benchmarking)
    def eventhandler(self, event, menuw=None):
        """
        Handles events in the tv guide
        """
        _debug_('eventhandler(event=%r, menuw=%r)' % (event.name, menuw), 2)

        ## MENU_CHANGE_STYLE
        if event == MENU_CHANGE_STYLE:
            if skin.toggle_display_style('tv'):
                start_time = self.start_time
                stop_time = self.stop_time
                start_channel = self.start_channel
                selected = self.selected

                self.n_items, hours_per_page = skin.items_per_page(
                    ('tv', self))

                before = -1
                after = -1
                for c in self.guide.chan_list:
                    if before >= 0 and after == -1:
                        before += 1
                    if after >= 0:
                        after += 1
                    if c.id == start_channel:
                        before = 0
                    if c.id == selected.channel_id:
                        after = 0

                if self.n_items <= before:
                    start_channel = selected.channel_id

                if after < self.n_items:
                    up = min(self.n_items - after, len(
                        self.guide.chan_list)) - 1
                    for i in range(len(self.guide.chan_list) - up):
                        if self.guide.chan_list[i + up].id == start_channel:
                            start_channel = self.guide.chan_list[i].id
                            break

                stop_time = start_time + hours_per_page * 60 * 60

                self.n_cols = (stop_time - start_time) / 60 / self.col_time
                self.rebuild(start_time, stop_time, start_channel, selected)
            return True

        ## MENU_UP: Move one channel up in the guide
        elif event == MENU_UP:
            self.change_channel(-1)
            return True

        ## MENU_DOWN: Move one channel down in the guide
        elif event == MENU_DOWN:
            self.change_channel(1)
            return True

        ## MENU_LEFT: Move to the next program on this channel
        elif event == MENU_LEFT:
            self.change_program(-1)
            return True

        ## MENU_RIGHT: Move to previous programm on this channel
        elif event == MENU_RIGHT:
            self.change_program(1)
            return True

        ## MENU_PAGEUP: Moves to the first of the currently displayed channels
        elif event == MENU_PAGEUP:
            self.change_channel(-self.n_items)
            return True

        ## MENU_PAGEDOWN: Move to the last of the currently displayed channels
        elif event == MENU_PAGEDOWN:
            self.change_channel(self.n_items)
            return True

        ## MENU_SUBMENU: Open a submenu for the selected program
        elif event == MENU_SUBMENU:
            # create a ProgramItem for the selected program
            pi = ProgramItem(self, prog=self.selected, context='guide')
            #and show its submenu
            pi.display_submenu(menuw=self.menuw)
            return True

        ## MENU_SELECT: Show the description
        elif event == MENU_SELECT:
            # create a ProgramItem for the selected program
            pi = ProgramItem(self, prog=self.selected, context='guide')
            #and show selecte the first action in the actions list
            pi.actions()[0][0](menuw=self.menuw)
            return True

        ## TV_START_RECORDING: add or remove this program from schedule
        elif event == TV_START_RECORDING:
            pi = ProgramItem(self, prog=self.selected, context='guide')
            pi.toggle_rec(menuw=self.menuw)
            return True

        ## PLAY: Start to watch the selected channel (if it is possible)
        elif event == PLAY:
            # create a ProgramItem for the selected program
            pi = ProgramItem(self, prog=self.selected, context='guide')
            #and show its submenu
            pi.play(menuw=self.menuw)
            return True

        ## PLAY_END: Show the guide again
        elif event == PLAY_END:
            self.show()
            return True

        # FIX or REMOVE:
        # the numerical INPUT events are not available in the tvmenu context
        ## numerical INPUT: Jump to a specific channel number
        elif event in INPUT_ALL_NUMBERS:
            newinput_time = time.time()
            if self.lastinput_value is not None:
                # allow 1.2 seconds delay for multiple digit channels
                if newinput_time - self.lastinput_time < 1.2:
                    # this enables multiple (max 3) digit channel selection
                    if self.lastinput_value >= 100:
                        self.lastinput_value = (self.lastinput_value % 100)
                    newinput_value = self.lastinput_value * 10 + int(event)
            self.lastinput_value = newinput_value
            self.lastinput_time = newinput_time

            channel_no = int(newinput_value) - 1
            if channel_no < len(self.guide.chan_list):
                self.start_channel = self.guide.chan_list[channel_no].id
            else:
                self.lastinput_value = None

            self.rebuild(self.start_time, self.stop_time, self.start_channel,
                         self.selected)
            return True

        return False

    ### gui functions

    def show(self):
        """ show the guide"""
        _debug_('show', 2)
        if not self.visible:
            self.visible = 1
            self.refresh()

    def hide(self):
        """ hide the guide"""
        _debug_('hide', 2)
        if self.visible:
            self.visible = 0
            skin.clear()

    @benchmark(benchmarking)
    def refresh(self, force_update=True):
        """refresh the guide

        This function is called automatically by freevo whenever this menu is
        opened or reopened.
        """
        _debug_('refresh(force_update=True)', 2)
        if self.menuw.children:
            return
        _debug_('tvguide: setting context to %s' % self.event_context, 2)
        rc.set_context(self.event_context)
        self.update(force_update)
        skin.draw(self.type, self)

    @benchmark(benchmarking)
    def update(self, force=False):
        """ update the guide

        This function updates the scheduled and overlap flags for
        all currently displayed programs.
        """
        _debug_('update(force=False)', 2)
        self.update_schedules(force)
        if self.table:
            for t in self.table:
                try:
                    for p in t.programs:
                        if p in self.scheduled_programs:
                            p.scheduled = True
                            # DO NOT change this to 'True' Twisted
                            # does not support boolean objects and
                            # it will break under Python 2.3
                        else:
                            p.scheduled = False
                            # Same as above; leave as 'False' until
                            # Twisted includes Boolean
                        if p in self.overlap_programs:
                            p.overlap = True
                        else:
                            p.overlap = False

                        if p in self.favorite_programs:
                            p.favorite = True
                        else:
                            p.favorite = False
                except:
                    pass

        #self.refresh()

    def jump_to_now(self, old_selected):
        """
        jump to now in the tv guide.
        """
        _debug_('jump_to_now(old_selected=%r)' % (old_selected, ), 2)
        start_time = time.time()
        stop_time = start_time + self.hours_per_page * 60 * 60
        start_channel = self.start_channel

        # we need to determine the program,
        # that is running now at the selected channel
        programs = self.guide.get_programs(start_time + 1, stop_time - 1,
                                           old_selected.channel_id)

        if len(programs) > 0 and len(programs[0].programs) > 0:
            selected = programs[0].programs[0]
        else:
            selected = None

        self.rebuild(start_time, stop_time, start_channel, selected)

    def advance_tv_guide(self, hours=0):
        """
        advance the tv guide by the number of hours that is passed in arg.
        """
        _debug_('advance_tv_guide(hours=%r)' % (hours, ), 2)
        new_start_time = self.start_time + (hours * 60 * 60)
        new_end_time = self.stop_time + (hours * 60 * 60)
        start_channel = self.start_channel

        # we need to determine the new selected program
        programs = self.guide.get_programs(new_start_time + 1,
                                           new_end_time - 1,
                                           self.start_channel)

        if len(programs) > 0 and len(programs[0].programs) > 0:
            selected = programs[0].programs[0]
        else:
            selected = None

        self.rebuild(new_start_time, new_end_time, start_channel, selected)

    @benchmark(benchmarking)
    def rebuild(self, start_time, stop_time, start_channel, selected):
        """ rebuild the guide

        This is neccessary we change the set of programs that have to be
        displayed, this is the case when the user moves around in the menu.
        """
        _debug_(
            'rebuild(start_time=%r, stop_time=%r, start_channel=%r, selected=%r)'
            % (start_time, stop_time, start_channel, selected), 2)
        self.guide = tv.epg_xmltv.get_guide()
        channels = self.guide.get_programs(start_time + 1, stop_time - 1)

        table = []

        self.start_time = start_time
        self.stop_time = stop_time
        self.start_channel = start_channel
        self.selected = selected

        self.display_up_arrow = False
        self.display_down_arrow = False

        # table header
        table += [['Chan']]
        for i in range(int(self.n_cols)):
            table[0] += [start_time + self.col_time * i * 60]

        table += [self.selected]  # the selected program

        found_1stchannel = 0
        if stop_time == None:
            found_1stchannel = 1

        flag_selected = 0

        n = 0
        for chan in channels:
            if n >= self.n_items:
                self.display_down_arrow = True
                break

            if start_channel != None and chan.id == start_channel:
                found_1stchannel = 1

            if not found_1stchannel:
                self.display_up_arrow = True

            if found_1stchannel:
                if not chan.programs:
                    prg = TvProgram(chan.id,
                                    0,
                                    0,
                                    2147483647,
                                    CHAN_NO_DATA,
                                    desc='')
                    chan.programs = [prg]

                for i in range(len(chan.programs)):
                    if selected:
                        if chan.programs[i] == selected:
                            flag_selected = 1

                table += [chan]
                n += 1

        if flag_selected == 0:
            for i in range(2, len(table)):
                if flag_selected == 1:
                    break
                else:
                    if table[i].programs:
                        for j in range(len(table[i].programs)):
                            if table[i].programs[j].stop > start_time:
                                self.selected = table[i].programs[j]
                                table[1] = table[i].programs[j]
                                flag_selected = 1
                                break

        self.table = table
        # then we can refresh the display with this programs
        #self.update()
        self.refresh(force_update=False)

    @benchmark(benchmarking)
    def change_program(self, value, full_scan=False):
        """
        Move to the next program
        """
        _debug_('change_program(value=%r, full_scan=%r)' % (value, full_scan),
                2)
        start_time = self.start_time
        stop_time = self.stop_time
        start_channel = self.start_channel
        last_prg = self.selected

        channel = self.guide.chan_dict[last_prg.channel_id]
        if full_scan:
            all_programs = self.guide.get_programs(start_time - 24 * 60 * 60,
                                                   stop_time + 24 * 60 * 60,
                                                   channel.id)
        else:
            all_programs = self.guide.get_programs(start_time + 1,
                                                   stop_time - 1, channel.id)

        # Current channel programs
        programs = all_programs[0].programs
        if programs:
            for i in range(len(programs)):
                if programs[i].title == last_prg.title and \
                   programs[i].start == last_prg.start and \
                   programs[i].stop == last_prg.stop and \
                   programs[i].channel_id == last_prg.channel_id:
                    break

            prg = None

            if value > 0:
                if i + value < len(programs):
                    prg = programs[i + value]
                elif full_scan:
                    prg = programs[-1]
                else:
                    return self.change_program(value, True)
            else:
                if i + value >= 0:
                    prg = programs[i + value]
                elif full_scan:
                    prg = programs[0]
                else:
                    return self.change_program(value, True)

            if prg.sub_title:
                procdesc = '"' + prg.sub_title + '"\n' + prg.desc
            else:
                procdesc = prg.desc
            to_info = (prg.title, procdesc)
            self.select_time = prg.start

            # set new (better) start / stop times
            extra_space = 0
            if prg.stop - prg.start > self.col_time * 60:
                extra_space = self.col_time * 60

            while prg.start + extra_space >= stop_time:
                start_time += (self.col_time * 60)
                stop_time += (self.col_time * 60)

            while prg.start + extra_space <= start_time:
                start_time -= (self.col_time * 60)
                stop_time -= (self.col_time * 60)
        else:
            prg = TvProgram(channel.id,
                            0,
                            0,
                            2147483647,
                            CHAN_NO_DATA,
                            desc='')
            to_info = CHAN_NO_DATA

        self.rebuild(start_time, stop_time, start_channel, prg)

    @benchmark(benchmarking)
    def change_channel(self, value):
        """
        Move to the next channel
        """
        _debug_('change_channel(value=%r)' % (value, ), 2)
        start_time = self.start_time
        stop_time = self.stop_time
        start_channel = self.start_channel
        last_prg = self.selected

        for i in range(len(self.guide.chan_list)):
            if self.guide.chan_list[i].id == start_channel:
                start_pos = i
                end_pos = i + self.n_items
            if self.guide.chan_list[i].id == last_prg.channel_id:
                break

        channel_pos = min(len(self.guide.chan_list) - 1, max(0, i + value))

        if value < 0 and channel_pos >= 0 and channel_pos < start_pos:
            start_channel = self.guide.chan_list[start_pos + value].id

        if value > 0 and channel_pos < len(
                self.guide.chan_list) - 1 and channel_pos + 1 >= end_pos:
            start_channel = self.guide.chan_list[start_pos + value].id

        channel = self.guide.chan_list[channel_pos]

        programs = self.guide.get_programs(start_time + 1, stop_time - 1,
                                           channel.id)
        programs = programs[0].programs

        prg = None
        if programs and len(programs) > 0:
            for i in range(len(programs)):
                if programs[i].stop > self.select_time and programs[
                        i].stop > start_time:
                    break

            prg = programs[i]
            if prg.sub_title:
                procdesc = '"' + prg.sub_title + '"\n' + prg.desc
            else:
                procdesc = prg.desc

            to_info = (prg.title, procdesc)
        else:
            prg = TvProgram(channel.id,
                            0,
                            0,
                            2147483647,
                            CHAN_NO_DATA,
                            desc='')
            to_info = CHAN_NO_DATA

        self.rebuild(start_time, stop_time, start_channel, prg)