def testFailAndStop(self):
        def foo(x):
            sc.stop()
            raise TestException(x)

        sc = ScheduledCall(foo, "bar")
        return self.assertFailure(sc.start(SimpleSchedule(0.1)), TestException)
Exemple #2
0
    def testFailAndStop(self):
        def foo(x):
            sc.stop()
            raise TestException(x)

        sc = ScheduledCall(foo, "bar")
        return self.assertFailure(sc.start(SimpleSchedule(0.1)), TestException)
 def set_schedule(self, schedule):
     "Set schedule and start playing"
     self.schedule = schedule
     self.scheduler_task = ScheduledCall(self.cue_next_program)
     self.scheduler_task.start(self)
     self.service.on_set_schedule(schedule)
     if not self.playing_program:
         self.resume_playback()
Exemple #4
0
    def testStopAtOnceLater(self):
        # Ensure that even when LoopingCall.stop() is called from a
        # reactor callback, it still prevents any subsequent calls.
        d = defer.Deferred()

        def foo():
            d.errback(
                failure.DefaultException(
                    "This task also should never get called."))

        self._sc = ScheduledCall(foo)
        self._sc.start(SimpleSchedule(1))
        reactor.callLater(0, self._callback_for_testStopAtOnceLater, d)
        return d
Exemple #5
0
 def set_schedule(self, schedule):
     "Set schedule and start playing"
     self.schedule = schedule
     self.scheduler_task = ScheduledCall(self.cue_next_program)
     self.scheduler_task.start(self)
     self.service.on_set_schedule(schedule)
     if not self.playing_program:
         self.resume_playback()
 def testStopAtOnceLater(self):
     # Ensure that even when LoopingCall.stop() is called from a
     # reactor callback, it still prevents any subsequent calls.
     d = defer.Deferred()
     def foo():
         d.errback(failure.DefaultException(
             "This task also should never get called."))
     self._sc = ScheduledCall(foo)
     self._sc.start(SimpleSchedule(1))
     reactor.callLater(0, self._callback_for_testStopAtOnceLater, d)
     return d
class ReactorLoopTestCase(unittest.TestCase):
    # Slightly inferior tests which exercise interactions with an actual
    # reactor.
    def testFailure(self):
        def foo(x):
            raise TestException(x)

        sc = ScheduledCall(foo, "bar")
        return self.assertFailure(sc.start(SimpleSchedule(0.1)), TestException)


    def testFailAndStop(self):
        def foo(x):
            sc.stop()
            raise TestException(x)

        sc = ScheduledCall(foo, "bar")
        return self.assertFailure(sc.start(SimpleSchedule(0.1)), TestException)


    def testStopAtOnceLater(self):
        # Ensure that even when LoopingCall.stop() is called from a
        # reactor callback, it still prevents any subsequent calls.
        d = defer.Deferred()
        def foo():
            d.errback(failure.DefaultException(
                "This task also should never get called."))
        self._sc = ScheduledCall(foo)
        self._sc.start(SimpleSchedule(1))
        reactor.callLater(0, self._callback_for_testStopAtOnceLater, d)
        return d


    def _callback_for_testStopAtOnceLater(self, d):
        self._sc.stop()
        reactor.callLater(0, d.callback, "success")

    def testWaitDeferred(self):
        # Tests if the callable isn't scheduled again before the returned
        # deferred has fired.
        timings = [0.2, 0.8]
        clock = task.Clock()

        def foo():
            d = defer.Deferred()
            d.addCallback(lambda _: sc.stop())
            clock.callLater(1, d.callback, None)
            return d

        sc = TestableScheduledCall(clock, foo)
        d = sc.start(SimpleSchedule(0.2))
        clock.advance(0.2)
        clock.pump(timings)
        self.failIf(clock.calls)

    def testFailurePropagation(self):
        # Tests if the failure of the errback of the deferred returned by the
        # callable is propagated to the sc errback.
        #
        # To make sure this test does not hang trial when LoopingCall does not
        # wait for the callable's deferred, it also checks there are no
        # calls in the clock's callLater queue.
        timings = [3]
        clock = task.Clock()

        def foo():
            d = defer.Deferred()
            clock.callLater(0.3, d.errback, TestException())
            return d

        sc = TestableScheduledCall(clock, foo)
        d = sc.start(SimpleSchedule(1))
        clock.pump(timings)
        self.assertFailure(d, TestException)

        clock.pump(timings)
        self.failIf(clock.calls)
        return d
class Playout(object):

    def __init__(self, service, player_class=None):
        if player_class is None:
            player_class = configuration.player_class
        self.player = _get_class(player_class)(LOOP_FILENAME)
        self.random_provider = jukebox.RandomProvider()
        self.service = service
        self.service.playout = self

        self.schedule = None
        self.next_program = None
        self.playing_program = None
        # A reference to the timed callback which aborts programs
        self.duration_call = None
        self.delayed_start_call = None
        # Still filename, if being displayed
        self.still = None
        # Temporary stack for sequence of videos before going to on_idle
        self.on_end_call_stack=[]

    def set_schedule(self, schedule):
        "Set schedule and start playing"
        self.schedule = schedule
        self.scheduler_task = ScheduledCall(self.cue_next_program)
        self.scheduler_task.start(self)
        self.service.on_set_schedule(schedule)
        if not self.playing_program:
            self.resume_playback()

    def resume_current_program(self):
        current_program = self.playing_program
        if current_program:
            self.cue_program(current_program, current_program.seconds_since_playback())

    def resume_playback(self):
        # TODO: Rename to resume_schedule
        current_program = self.schedule.get_current_program()
        if current_program:
            self.cue_program(current_program, current_program.seconds_since_playback())
        else:
            self.on_idle()

    def cue_next_program(self):
        """Starts the next program

        Set the next program with Playout.set_next_program"""
        if self.next_program:
            self.cue_program(self.next_program)
            self.next_program = None

    def _cancel_pending_calls(self):
        """Stops any pending calls from starting in the future (and disrupt playback)

        This is used whenever a program is started and new calls will be registered
        """
        if self.duration_call and not self.duration_call.called:
            self.duration_call.cancel()
            self.duration_call = None
        if self.delayed_start_call and not self.delayed_start_call.called:
            self.delayed_start_call.cancel()
            self.delayed_start_call = None

    def cue_program(self, program, resume_offset=0):
        """Starts the given program"""
        self._cancel_pending_calls()
        duration_text = "Unknown"
        if program.playback_duration == float("inf"):
            duration_text = "Infinite"
        elif program.playback_duration:
            duration_text = "%i:%02i" % (program.playback_duration / 60, program.playback_duration % 60)
            # Schedule next call
            delta = program.playback_duration-resume_offset
            if delta <= 0.0:
                self.player.pause_screen()
            else:
                self.duration_call = reactor.callLater(delta, self.on_program_ended)
        logging.info("Playback video_id=%i, offset=%s+%ss name='%s' duration=%s" % (
            program.media_id,
            str(program.playback_offset), str(resume_offset), program.title, duration_text))
        # Start playback
        self.player.play_program(program, resume_offset=resume_offset)
        self.service.on_playback_started(program)
        self.playing_program = program

    def set_next_program(self, program):
        self.next_program = program
        self.service.on_set_next_program(program)
        if program:
            logging.info("Next scheduled video_id=%i @ %s" % (
                program.media_id, program.program_start))
        else:
            logging.warning("Scheduled nothing")

    # ISchedule.getDelayForNext
    def getDelayForNext(self):
        # Queue next
        program = self.schedule.get_next_program()
        if program == None:
            self.scheduler_task.stop()
            self.set_next_program(None)
            # This will actually call cue_next_program once more.
            logging.warning("Program schedule empty")
            return 0.0
        self.set_next_program(program)
        return program.seconds_until_playback()

    def stop_schedule(self):
        if self.scheduler_task and self.scheduler_task.running:
            self.scheduler_task.stop()
        #self.set_next_program(None)

    def start_schedule(self):
        self.stop_schedule()
        self.scheduler_task = ScheduledCall(self.cue_next_program)
        self.scheduler_task.start(self)

    def show_still(self, filename="stills/tekniskeprover.png"):
        self._cancel_pending_calls()
        self.stop_schedule()
        self.player.show_still(filename)
        self.service.on_still(filename)
        logging.info("Show still: %s", filename)

    def cancel_still(self):
        logging.info("Cancel still")
        self.service.on_still("")
        self.start_schedule()
        # TODO: resume_current_program?
        self.resume_playback()

    def on_program_ended(self):
        """
        try:
            logging.debug("Video '%s' #%i ended with %.1fs left. " % (
                self.playing_program.title, self.playing_program.media_id,
                self.player.seconds_until_end_of_playing_video())
                )
            pass
        # TODO: Add proper exception/exceptionlogging
        except:
            logging.warning("Excepted while trying to log on_program_ended")
        """
        if self.on_end_call_stack:
            func = self.on_end_call_stack.pop(0)
            func()
        else:
            self.on_idle()

    def play_jukebox(self):
        logging.info("Jukebox playback start")
        program = self.schedule.new_program()
        limit = 90*60 # 90 minutes long programs max
        if self.next_program:
            limit = min(limit, self.next_program.seconds_until_playback())
        video = self.random_provider.get_random_video(limit)
        program.set_program(
            media_id=video["media_id"],
            program_start=clock.now(),
            playback_duration=video["duration"],
            title=video["name"])
        self.cue_program(program)

    def play_ident(self):
        logging.info("Ident playback start")
        program = self.schedule.new_program()
        program.set_program(
            media_id=-1,
            program_start=clock.now(),
            playback_duration=IDENT_LENGTH,
            title="Frikanalen Vignett",
            filename=IDENT_FILENAME)
        self.cue_program(program)

    def on_idle(self):
        time_until_next = float("inf")
        if self.next_program:
            time_until_next = self.next_program.seconds_until_playback()
        # The rules.
        use_jukebox = configuration.jukebox
        use_jukebox &= time_until_next > (120+IDENT_LENGTH)
        use_jukebox &= self.random_provider.enough_room(time_until_next)
        if use_jukebox:
            loop_length = 12.0
            PAUSE_LENGTH = IDENT_LENGTH+loop_length
            logging.info("Pause before jukebox: %.1fs" % PAUSE_LENGTH)
            program = self.schedule.new_program()
            program.set_program(-1,
                program_start=clock.now(),
                playback_duration=loop_length,
                title="Jukebox pause screen",
                filename=LOOP_FILENAME,
                loop=True)
            self.cue_program(program)
            self.on_end_call_stack.append(self.play_ident)
            self.on_end_call_stack.append(self.play_jukebox)
        elif time_until_next >= 12+IDENT_LENGTH:
            logging.info("Pause idle: %.1fs" % time_until_next)
            PAUSE_LENGTH = time_until_next
            program = self.schedule.new_program()
            program.set_program(-1,
                program_start=clock.now(),
                playback_duration=time_until_next-IDENT_LENGTH,
                title="Pause screen",
                filename=LOOP_FILENAME,
                loop=True)
            self.cue_program(program)
            self.on_end_call_stack.append(self.play_ident)
        else:
            logging.info("Short idle: %.1fs" % time_until_next)
            # Show pausescreen
            program = self.schedule.new_program()
            t = None
            if self.next_program:
                t = self.next_program.seconds_until_playback()
            program.set_program(-1, program_start=clock.now(), playback_duration=t, title="Pause screen", filename=LOOP_FILENAME, loop=True)
            #self.cue_program(program) # TODO: Doesn't handle looping
            self.player.pause_screen()
            self.playing_program = program
            self.service.on_playback_started(program)
 def start_schedule(self):
     self.stop_schedule()
     self.scheduler_task = ScheduledCall(self.cue_next_program)
     self.scheduler_task.start(self)
Exemple #10
0
 def testDefaultClock(self):
     """
     L{LoopingCall}'s default clock should be the reactor.
     """
     call = ScheduledCall(lambda: None)
     self.assertEqual(call.clock, reactor)
Exemple #11
0
class Playout(object):
    zope.interface.implements(ISchedule)

    def __init__(self, service, player_class=None):
        if player_class is None:
            player_class = configuration.player_class
        self.player = _get_class(player_class)(LOOP_FILENAME)
        self.random_provider = jukebox.RandomProvider()
        self.service = service
        self.service.playout = self

        self.schedule = None
        self.next_program = None
        self.playing_program = None
        # A reference to the timed callback which aborts programs
        self.duration_call = None
        self.delayed_start_call = None
        # Still filename, if being displayed
        self.still = None
        # Temporary stack for sequence of videos before going to on_idle
        self.on_end_call_stack=[]

    def set_schedule(self, schedule):
        "Set schedule and start playing"
        self.schedule = schedule
        self.scheduler_task = ScheduledCall(self.cue_next_program)
        self.scheduler_task.start(self)
        self.service.on_set_schedule(schedule)
        if not self.playing_program:
            self.resume_playback()

    def resume_current_program(self):
        current_program = self.playing_program
        if current_program:
            self.cue_program(current_program, current_program.seconds_since_playback())

    def resume_playback(self):
        # TODO: Rename to resume_schedule
        current_program = self.schedule.get_current_program()
        if current_program:
            self.cue_program(current_program, current_program.seconds_since_playback())
        else:
            self.on_idle()

    def cue_next_program(self):
        """Starts the next program

        Set the next program with Playout.set_next_program"""
        if self.next_program:
            self.cue_program(self.next_program)
            self.next_program = None

    def _cancel_pending_calls(self):
        """Stops any pending calls from starting in the future (and disrupt playback)

        This is used whenever a program is started and new calls will be registered
        """
        if self.duration_call and not self.duration_call.called:
            self.duration_call.cancel()
            self.duration_call = None
        if self.delayed_start_call and not self.delayed_start_call.called:
            self.delayed_start_call.cancel()
            self.delayed_start_call = None

    def cue_program(self, program, resume_offset=0):
        """Starts the given program"""
        self._cancel_pending_calls()
        duration_text = "Unknown"
        if program.playback_duration == float("inf"):
            duration_text = "Infinite"
        elif program.playback_duration:
            duration_text = "%i:%02i" % (program.playback_duration / 60, program.playback_duration % 60)
            # Schedule next call
            delta = program.playback_duration-resume_offset
            if delta <= 0.0:
                self.player.pause_screen()
            else:
                self.duration_call = reactor.callLater(delta, self.on_program_ended)
        logging.info("Playback video_id=%i, offset=%s+%ss name='%s' duration=%s" % (
            program.media_id,
            str(program.playback_offset), str(resume_offset), program.title, duration_text))
        # Start playback
        self.player.play_program(program, resume_offset=resume_offset)
        self.service.on_playback_started(program)
        self.playing_program = program

    def set_next_program(self, program):
        self.next_program = program
        self.service.on_set_next_program(program)
        if program:
            logging.info("Next scheduled video_id=%i @ %s" % (
                program.media_id, program.program_start))
        else:
            logging.warning("Scheduled nothing")

    # ISchedule.getDelayForNext
    def getDelayForNext(self):
        # Queue next
        program = self.schedule.get_next_program()
        if program == None:
            self.scheduler_task.stop()
            self.set_next_program(None)
            # This will actually call cue_next_program once more.
            logging.warning("Program schedule empty")
            return 0.0
        self.set_next_program(program)
        return program.seconds_until_playback()

    def stop_schedule(self):
        if self.scheduler_task and self.scheduler_task.running:
            self.scheduler_task.stop()
        #self.set_next_program(None)

    def start_schedule(self):
        self.stop_schedule()
        self.scheduler_task = ScheduledCall(self.cue_next_program)
        self.scheduler_task.start(self)

    def show_still(self, filename="stills/tekniskeprover.png"):
        self._cancel_pending_calls()
        self.stop_schedule()
        self.player.show_still(filename)
        self.service.on_still(filename)
        logging.info("Show still: %s", filename)

    def cancel_still(self):
        logging.info("Cancel still")
        self.service.on_still("")
        self.start_schedule()
        # TODO: resume_current_program?
        self.resume_playback()

    def on_program_ended(self):
        """
        try:
            logging.debug("Video '%s' #%i ended with %.1fs left. " % (
                self.playing_program.title, self.playing_program.media_id,
                self.player.seconds_until_end_of_playing_video())
                )
            pass
        # TODO: Add proper exception/exceptionlogging
        except:
            logging.warning("Excepted while trying to log on_program_ended")
        """
        if self.on_end_call_stack:
            func = self.on_end_call_stack.pop(0)
            func()
        else:
            self.on_idle()

    def play_jukebox(self):
        logging.info("Jukebox playback start")
        program = self.schedule.new_program()
        limit = 90*60 # 90 minutes long programs max
        if self.next_program:
            limit = min(limit, self.next_program.seconds_until_playback())
        video = self.random_provider.get_random_video(limit)
        program.set_program(
            media_id=video["media_id"],
            program_start=clock.now(),
            playback_duration=video["duration"],
            title=video["name"])
        self.cue_program(program)

    def play_ident(self):
        logging.info("Ident playback start")
        program = self.schedule.new_program()
        program.set_program(
            media_id=-1,
            program_start=clock.now(),
            playback_duration=IDENT_LENGTH,
            title="Frikanalen Vignett",
            filename=IDENT_FILENAME)
        self.cue_program(program)

    def on_idle(self):
        time_until_next = float("inf")
        if self.next_program:
            time_until_next = self.next_program.seconds_until_playback()
        # The rules.
        use_jukebox = configuration.jukebox
        use_jukebox &= time_until_next > (120+IDENT_LENGTH)
        use_jukebox &= self.random_provider.enough_room(time_until_next)
        if use_jukebox:
            loop_length = 12.0
            PAUSE_LENGTH = IDENT_LENGTH+loop_length
            logging.info("Pause before jukebox: %.1fs" % PAUSE_LENGTH)
            program = self.schedule.new_program()
            program.set_program(-1,
                program_start=clock.now(),
                playback_duration=loop_length,
                title="Jukebox pause screen",
                filename=LOOP_FILENAME,
                loop=True)
            self.cue_program(program)
            self.on_end_call_stack.append(self.play_ident)
            self.on_end_call_stack.append(self.play_jukebox)
        elif time_until_next >= 12+IDENT_LENGTH:
            logging.info("Pause idle: %.1fs" % time_until_next)
            PAUSE_LENGTH = time_until_next
            program = self.schedule.new_program()
            program.set_program(-1,
                program_start=clock.now(),
                playback_duration=time_until_next-IDENT_LENGTH,
                title="Pause screen",
                filename=LOOP_FILENAME,
                loop=True)
            self.cue_program(program)
            self.on_end_call_stack.append(self.play_ident)
        else:
            logging.info("Short idle: %.1fs" % time_until_next)
            # Show pausescreen
            program = self.schedule.new_program()
            t = None
            if self.next_program:
                t = self.next_program.seconds_until_playback()
            program.set_program(-1, program_start=clock.now(), playback_duration=t, title="Pause screen", filename=LOOP_FILENAME, loop=True)
            #self.cue_program(program) # TODO: Doesn't handle looping
            self.player.pause_screen()
            self.playing_program = program
            self.service.on_playback_started(program)
Exemple #12
0
class ReactorLoopTestCase(unittest.TestCase):
    # Slightly inferior tests which exercise interactions with an actual
    # reactor.
    def testFailure(self):
        def foo(x):
            raise TestException(x)

        sc = ScheduledCall(foo, "bar")
        return self.assertFailure(sc.start(SimpleSchedule(0.1)), TestException)

    def testFailAndStop(self):
        def foo(x):
            sc.stop()
            raise TestException(x)

        sc = ScheduledCall(foo, "bar")
        return self.assertFailure(sc.start(SimpleSchedule(0.1)), TestException)

    def testStopAtOnceLater(self):
        # Ensure that even when LoopingCall.stop() is called from a
        # reactor callback, it still prevents any subsequent calls.
        d = defer.Deferred()

        def foo():
            d.errback(
                failure.DefaultException(
                    "This task also should never get called."))

        self._sc = ScheduledCall(foo)
        self._sc.start(SimpleSchedule(1))
        reactor.callLater(0, self._callback_for_testStopAtOnceLater, d)
        return d

    def _callback_for_testStopAtOnceLater(self, d):
        self._sc.stop()
        reactor.callLater(0, d.callback, "success")

    def testWaitDeferred(self):
        # Tests if the callable isn't scheduled again before the returned
        # deferred has fired.
        timings = [0.2, 0.8]
        clock = task.Clock()

        def foo():
            d = defer.Deferred()
            d.addCallback(lambda _: sc.stop())
            clock.callLater(1, d.callback, None)
            return d

        sc = TestableScheduledCall(clock, foo)
        d = sc.start(SimpleSchedule(0.2))
        clock.advance(0.2)
        clock.pump(timings)
        self.failIf(clock.calls)

    def testFailurePropagation(self):
        # Tests if the failure of the errback of the deferred returned by the
        # callable is propagated to the sc errback.
        #
        # To make sure this test does not hang trial when LoopingCall does not
        # wait for the callable's deferred, it also checks there are no
        # calls in the clock's callLater queue.
        timings = [3]
        clock = task.Clock()

        def foo():
            d = defer.Deferred()
            clock.callLater(0.3, d.errback, TestException())
            return d

        sc = TestableScheduledCall(clock, foo)
        d = sc.start(SimpleSchedule(1))
        clock.pump(timings)
        self.assertFailure(d, TestException)

        clock.pump(timings)
        self.failIf(clock.calls)
        return d
Exemple #13
0
 def testBadDelay(self):
     sc = ScheduledCall(lambda: None)
     self.assertRaises(TypeError, sc.start, -1)
Exemple #14
0
class Playout(object):
    # ISchedule.getDelayForNext
    # This is called by ScheduledCall to determine the number
    # of seconds until the next time self.cue_next_program is
    # invoked
    def getDelayForNext(self):
        # Queue next
        next_program = self.schedule.get_next_program()
        if next_program == None:
            self.scheduler_task.stop()
            self.set_next_program(None)
            # This will actually call cue_next_program once more.
            logging.warning("Program schedule empty")
            return 0.0
        self.set_next_program(next_program)
        return next_program.seconds_until_playback()

    def __init__(self, player_class=None):
        if player_class is None:
            player_class = configuration.playerClass
        self.player = _get_class(player_class)(LOOP_FILENAME)
        self.random_provider = jukebox.RandomProvider()

        self.schedule = None
        self.next_program = None
        self.playing_program = None

        # The video_end_timeout is invoked at a video's end time because
        # the current architecture does not receive media end messages
        # from CasparCG
        self.video_end_timeout = None

        # Temporary stack for sequence of videos before going to on_idle
        self.programEndCallbackStack = []

    def load_schedule(self):
        "Set schedule and start playing"
        logging.debug("Attaching schedule")
        self.schedule = schedulestore.load(clock.now().date(), numDays=1)

        self.start_schedule()
        self.self_pending_refresh = reactor.callLater(60 * 60,
                                                      self.load_schedule)

        if not self.playing_program:
            self.resume_schedule()

    def resume_current_program(self):
        current_program = self.schedule.get_current_program()
        logging.debug("in resume_current_program; current_program=%s",
                      current_program)
        if current_program:
            logging.debug(
                "resume_current_program invoking cue_program({})".format(
                    current_program))
            self.cue_program(current_program,
                             current_program.seconds_since_scheduled_start())

    def resume_schedule(self):
        """Resumes the currently scheduled program if there is one,
        otherwise it goes into idle mode"""

        if self.schedule.get_current_program():
            self.resume_current_program()
        else:
            self.on_idle()

    def cue_next_program(self):
        """Starts the next program

        Set the next program with Playout.set_next_program"""
        logging.debug("In cue_next_program - self.next_program=%s",
                      self.next_program)
        if self.next_program:
            self.cue_program(self.next_program)
            self.next_program = None

    def _clear_timeouts(self):
        """Clears any previously set timeouts"""
        logging.debug("in _clear_timeouts")
        if self.video_end_timeout and not self.video_end_timeout.called:
            logging.debug("clearing timeouts")
            self.video_end_timeout.cancel()
            self.video_end_timeout = None
        else:
            logging.debug("not clearing timeout")

    def _fmt_duration(self, duration):
        if duration == float("inf"):
            return "Infinite"

        return "%i:%02i" % (duration / 60, duration % 60)

    def cue_program(self, program, seekSeconds=0):
        logging.debug("In cue_program, program={}, offset={}".format(
            program, seekSeconds))
        logging.debug("schedule.programs = %s", self.schedule.programs)
        # Clear any timeouts set for the previous program
        self._clear_timeouts()

        if program.playback_duration != float("inf"):
            secondsUntilEnd = program.playback_duration - seekSeconds

            if secondsUntilEnd <= 0.0:
                errormsg = """secondsUntilEnd == {} for program {}"""
                logging.error(errormsg.format(secondsUntilEnd, program))
            else:
                errormsg = """secondsUntilEnd == {} for program {}"""
                logging.debug(errormsg.format(secondsUntilEnd, program))
                self.video_end_timeout = reactor.callLater(
                    secondsUntilEnd, self.on_program_ended)

        if seekSeconds > 0:
            logging.info("Playing program {}, seeking {}s".format(
                program, seekSeconds))
        else:
            logging.info("Playing program {}".format(program))

        # Start playback
        self.player.play_program(program, resume_offset=seekSeconds)
        self.playing_program = program

    def set_next_program(self, program):
        self.next_program = program
        if program:
            logging.info("Next scheduled video_id=%i @ %s" %
                         (program.media_id, program.program_start))
        else:
            logging.warning("Scheduled nothing")

    def stop_schedule(self):
        logging.info("Stopping schedule")
        try:
            if self.scheduler_task.running:
                self.scheduler_task.stop()
        except AttributeError:
            pass

    def start_schedule(self):
        logging.info("Starting schedule (will stop the old one if it exists)")
        self.stop_schedule()
        self.scheduler_task = ScheduledCall(self.cue_next_program)
        self.scheduler_task.start(self)

    def on_program_ended(self):
        logging.debug("on_program_ended invoked")

        if self.programEndCallbackStack:
            self.programEndCallbackStack.pop(0)()
        else:
            self.on_idle()

    def play_jukebox(self):
        logging.info("Jukebox playback start")
        limit = 90 * 60  # 90 minutes long programs max
        if self.next_program:
            limit = min(limit, self.next_program.seconds_until_playback())
        video = self.random_provider.get_random_video(limit)
        program = Program(media_id=video["media_id"],
                          program_start=clock.now(),
                          playback_duration=video["duration"],
                          title=video["name"])
        logging.debug("jukebox invoking cue_program({})".format(program))
        self.cue_program(program)

    def play_ident(self):
        logging.info("Ident playback start [skipped]")
        program = Program(
            media_id=-1,
            program_start=clock.now(),
            playback_duration=2,  #IDENT_LENGTH,
            title="Frikanalen Vignett",
            filename=IDENT_FILENAME)
        logging.debug("play_ident invoking cue_program({})".format(program))
        self.cue_program(program)

    def on_idle(self):
        time_until_next = float("inf")
        if self.next_program:
            time_until_next = self.next_program.seconds_until_playback()
        logging.info("on_idle is invoked, with %.1fs left until next item" %\
               (time_until_next,))
        # The rules.
        use_jukebox = configuration.useJukebox
        use_jukebox &= time_until_next > (120 + IDENT_LENGTH)
        use_jukebox &= self.random_provider.enough_room(time_until_next)
        if use_jukebox:
            loop_length = 12.0
            #PAUSE_LENGTH = IDENT_LENGTH+loop_length
            PAUSE_LENGTH = 2  #loop_length
            logging.info("Pause before jukebox: %.1fs" % PAUSE_LENGTH)
            program = Program(
                media_id=-1,
                program_start=clock.now(),
                playback_duration=1,  #loop_length,
                title="Jukebox pause screen",
                filename=LOOP_FILENAME,
                loop=True)
            logging.debug("on_idle invoking cue_program({})".format(program))
            self.cue_program(program)
            self.programEndCallbackStack.append(self.play_ident)
            self.programEndCallbackStack.append(self.play_jukebox)
        elif time_until_next >= 12 + IDENT_LENGTH:
            logging.info("Pause idle: %.1fs" % time_until_next)
            PAUSE_LENGTH = time_until_next
            program = Program(
                media_id=-1,
                program_start=clock.now(),
                playback_duration=time_until_next - IDENT_LENGTH,
                title="Pause screen",
                filename='[HTML]" "https://frikanalen.no/graphics',
                loop=True)
            logging.debug("on_idle invoking cue_program({})".format(program))
            self.cue_program(program)
            self.programEndCallbackStack.append(self.play_ident)
        else:
            logging.info("Short idle: %.1fs" % time_until_next)
            # Show pausescreen
            t = None
            if self.next_program:
                t = self.next_program.seconds_until_playback()
            program = Program(media_id=-1,
                              program_start=clock.now(),
                              playback_duration=t,
                              title="Pause screen",
                              filename=LOOP_FILENAME,
                              loop=True)
            #self.cue_program(program) # TODO: Doesn't handle looping
            self.player.pause_screen()
            self.playing_program = program
Exemple #15
0
 def start_schedule(self):
     logging.info("Starting schedule (will stop the old one if it exists)")
     self.stop_schedule()
     self.scheduler_task = ScheduledCall(self.cue_next_program)
     self.scheduler_task.start(self)
Exemple #16
0
 def __init__(self, clock, *a, **kw):
     ScheduledCall.__init__(self, *a, **kw)
     self.clock = clock
Exemple #17
0
 def __init__(self, clock, *a, **kw):
     ScheduledCall.__init__(self,*a, **kw)
     self.clock = clock
Exemple #18
0
 def start_schedule(self):
     self.stop_schedule()
     self.scheduler_task = ScheduledCall(self.cue_next_program)
     self.scheduler_task.start(self)