def test_timebar_with_marker(self):
        tb = TimeBar(100, 102)

        assert len(tb.get_timebar(0)) == 102
        assert tb.get_timebar(0).index("o") == 1
        assert tb.get_timebar(0) == "<o                                                                                                   >"

        assert len(tb.get_timebar(10)) == 102
        assert tb.get_timebar(10).index("o") == 11
        assert tb.get_timebar(10) == "<          o                                                                                         >"

        assert len(tb.get_timebar(99)) == 102
        assert tb.get_timebar(99).index("o") == 100

        assert len(tb.get_timebar(100)) == 102
        assert tb.get_timebar(100).index("o") == 100
Example #2
0
    def test_timebar_with_marker(self):
        tb = TimeBar(100, 102)

        assert len(tb.get_timebar(0)) == 102
        assert tb.get_timebar(0).index("o") == 1
        assert tb.get_timebar(
            0
        ) == "<o                                                                                                   >"

        assert len(tb.get_timebar(10)) == 102
        assert tb.get_timebar(10).index("o") == 11
        assert tb.get_timebar(
            10
        ) == "<          o                                                                                         >"

        assert len(tb.get_timebar(99)) == 102
        assert tb.get_timebar(99).index("o") == 100

        assert len(tb.get_timebar(100)) == 102
        assert tb.get_timebar(100).index("o") == 100
class VT100Player(object):
    """
    Some escape codes used within VT100 Streams
    @see: http://ascii-table.com/ansi-escape-sequences-vt-100.php
    """
    ESC = chr(27)  # VT100 escape character constant
    JMPHOME = ESC + "[H"  # Move cursor to upper left corner
    CLEARSCRN = ESC + "[2J"  # Clear entire screen
    CLEARDOWN = ESC + "[J"  # Clear screen from cursor down

    def __init__(self, movie, framerate=24):
        """
        Player class plays a movie.
        It also stores the current position.
        It exposes the all frame numbers in real values. Therefore not encoded.

        Args:
            movie (ascii_movie.Movie): Movie Object that the player will play.

        """
        self._movie = movie
        self._cursor = 0  # virtual cursor pointing to the current frame
        self._frame_count = 0
        self._framerate = int(framerate)

        self._stopped = False

        self._clear_screen_setup_done = False

        for f in self._movie.frames:
            self._frame_count += f.display_time

        self.timebar = TimeBar(self._frame_count, self._movie.screen_width)

    def play(self):
        """
        Plays the movie

        The 'very long' 'if' condition is for the 
        stability of the frame rate

        If the actuall time(unpredictable) is not in the time window of the current frame,
        just skip this frame
        """
        self._stopped = False
        oldTime = time.time()
        r = self._framerate
        for frame in self._movie.frames:
            if self._stopped:
                return
            if (time.time() - oldTime) * r >= self._cursor and (time.time(
            ) - oldTime) * r < self._cursor + frame.display_time:
                self._cursor += frame.display_time
                self._load_frame(frame, self._cursor)
                time.sleep((self._cursor - (time.time() - oldTime) * r) / r)
            else:
                """
                Skip playing this frame and continue as if
                it has been played

                Due to 'time.sleep', the 'else' clause can only be reached 
                when the second condition is violated
                """
                self._cursor += frame.display_time

    def stop(self):
        """
        Stop the movie
        """
        self._stopped = True

    def _load_frame(self, frame, frame_pos):
        """
        Buffer the the frame and then call draw_frame to display it

        Args:
            frame (ascii_movie.Frame): Frame data to display
            frame_pos (int):  Where the frame falls in the movie

        """
        screenbuf = BytesIO()
        if not self._clear_screen_setup_done:
            screenbuf.write(self.CLEARSCRN.encode())
            self._clear_screen_setup_done = True

        # center vertical, with respect to the time bar (like letter boxing)
        screenbuf.write(self._move_cursor(1, self._movie.top_margin))
        for line in frame.data:
            screenbuf.write((line + "\r\n").encode())

        self._update_timebar(screenbuf, frame_pos)

        # now rewind the internal buffer and fire the public event
        screenbuf.seek(0)
        self.draw_frame(screenbuf)

    def draw_frame(self, screen_buffer):
        """
        Public event method, which can be used to get new Screens.
        This must be implemented by the user.

        Args:
            screen_buffer:  its a file like object containing the VT100 screen buffer

        """
        raise NotImplementedError("You must specify how to draw the frame.")

    def _update_timebar(self, screen_buffer, frame_pos):
        """
        Writes at the bottom of the screen a line like this
        <.......o.....................>
        It should visualize a timeline with 'o' is the current position.

        Args:
            screen_buffer: file like object, where the data is written to
            frame_pos (int): current cursor position on frame

        """
        # Move cursor to the bottom of the screen
        screen_buffer.write(self._move_cursor(1, self._movie.screen_height))

        screen_buffer.write(self.timebar.get_timebar(frame_pos).encode())

    def _move_cursor(self, x, y):
        """
        Send VT100 commands: go to position X,Y

        Args:
            x (int): x coordinate (starting at 1)
            y (int): y coordinate (starting at 1)

        Returns:
            str: the VT100 code as a string

        """
        if 0 >= x > self._movie.screen_width or 0 >= y > self._movie.screen_height:
            sys.stderr.write(
                "Warning, coordinates out of range. ({0}, {1})\n".format(x, y))
            return "".encode()
        else:
            return (self.ESC + "[{0};{1}H".format(y, x)).encode()
class VT100Player(object):
    """
    Some escape codes used within VT100 Streams
    @see: http://ascii-table.com/ansi-escape-sequences-vt-100.php
    """
    ESC = chr(27)  # VT100 escape character constant
    JMPHOME = ESC + "[H"  # Move cursor to upper left corner
    CLEARSCRN = ESC + "[2J"  # Clear entire screen
    CLEARDOWN = ESC + "[J"  # Clear screen from cursor down

    def __init__(self, movie: Movie):
        """
        Player class plays a movie.
        It also stores the current position.
        It exposes the all frame numbers in real values. Therefore not encoded.

        Args:
            movie (ascii_movie.Movie): Movie Object that the player will play.

        """
        self._movie = movie
        self._cursor = 0  # virtual cursor pointing to the current frame
        self._frame_count = 0

        self._stopped = False

        self._clear_screen_setup_done = False

        for f in self._movie.frames:
            self._frame_count += f.display_time

        self.timebar = TimeBar(self._frame_count, self._movie.screen_width)

    def play(self):
        """
        Plays the movie
        """
        self._stopped = False
        drift = 0
        dropped_frames = 0
        dropped_seconds = 0
        destyling_applied = False
        movie = self._movie.clone()
        for frame in movie.frames:
            if self._stopped:
                return
            self._cursor += frame.display_time
            # We'll drop some frames to catch up, if we need to
            if frame.frame_seconds <= drift:
                drift -= frame.frame_seconds
                dropped_frames += 1
                dropped_seconds += frame.frame_seconds
                continue  # Skip this frame and don't even render it

            right_now = datetime.now()
            self._load_frame(frame, self._cursor)
            draw_time = datetime.now() - right_now
            sleep_time = frame.frame_seconds - draw_time.total_seconds()
            if sleep_time < 0:
                # When draw speed exceeds the total frame seconds, we record the drift so we can catch up later
                drift += abs(sleep_time)
                sleep_time = 0  # Don't really sleep at all if it's already taking too long
            elif sleep_time > 0:
                # When draw speed exceeds total frame seconds, we catch up, if there's catching up to do
                drift -= min(frame.frame_seconds, drift)
            if drift == 0:
                dropped_seconds = 0
            elif dropped_seconds > DESTYLING_THRESHOLD_SECONDS and not destyling_applied:
                movie.remove_styling()
                print("Destyling applied to speed transmission")
                destyling_applied = True

            time.sleep(sleep_time)
        print(f"Dropped {dropped_frames} frames to speed connection")

    def stop(self):
        """
        Stop the movie
        """
        self._stopped = True

    def _load_frame(self, frame, frame_pos):
        """
        Buffer the the frame and then call draw_frame to display it

        Args:
            frame (ascii_movie.Frame): Frame data to display
            frame_pos (int):  Where the frame falls in the movie

        """
        screenbuf = BytesIO()
        if not self._clear_screen_setup_done:
            screenbuf.write(self.CLEARSCRN.encode())
            self._clear_screen_setup_done = True

        # center vertical, with respect to the time bar (like letter boxing)
        screenbuf.write(self._move_cursor(1, self._movie.top_margin))
        for line in frame.data:
            screenbuf.write((line + "\r\n").encode())

        self._update_timebar(screenbuf, frame_pos)

        # now rewind the internal buffer and fire the public event
        screenbuf.seek(0)
        self.draw_frame(screenbuf)

    def draw_frame(self, screen_buffer):
        """
        Public event method, which can be used to get new Screens.
        This must be implemented by the user.

        Args:
            screen_buffer:  its a file like object containing the VT100 screen buffer

        """
        raise NotImplementedError("You must specify how to draw the frame.")

    def _update_timebar(self, screen_buffer, frame_pos):
        """
        Writes at the bottom of the screen a line like this
        <.......o.....................>
        It should visualize a timeline with 'o' is the current position.

        Args:
            screen_buffer: file like object, where the data is written to
            frame_pos (int): current cursor position on frame

        """
        # Move cursor to the bottom of the screen
        screen_buffer.write(self._move_cursor(1, self._movie.screen_height))

        screen_buffer.write(self.timebar.get_timebar(frame_pos).encode())

    def _move_cursor(self, x, y):
        """
        Send VT100 commands: go to position X,Y

        Args:
            x (int): x coordinate (starting at 1)
            y (int): y coordinate (starting at 1)

        Returns:
            str: the VT100 code as a string

        """
        if 0 >= x > self._movie.screen_width or 0 >= y > self._movie.screen_height:
            sys.stderr.write(
                "Warning, coordinates out of range. ({0}, {1})\n".format(x, y))
            return "".encode()
        else:
            return (self.ESC + "[{0};{1}H".format(y, x)).encode()
Example #5
0
    def test_timebar_with_marker_short(self):
        tb = TimeBar(100, 8)

        assert len(tb.get_timebar(0)) == 8
        assert tb.get_timebar(0).index("o") == 1
        assert tb.get_timebar(0) == "<o     >"

        assert len(tb.get_timebar(10)) == 8
        assert tb.get_timebar(10).index("o") == 2
        assert tb.get_timebar(10) == "< o    >"

        assert len(tb.get_timebar(50)) == 8
        assert tb.get_timebar(50).index("o") == 4
        assert tb.get_timebar(50) == "<   o  >"

        assert len(tb.get_timebar(99)) == 8
        assert tb.get_timebar(99).index("o") == 6
        assert tb.get_timebar(99) == "<     o>"

        assert len(tb.get_timebar(100)) == 8
        assert tb.get_timebar(100).index("o") == 6
        assert tb.get_timebar(100) == "<     o>"
Example #6
0
 def test_frame_num_larger_than_timebar_frame_count(self):
     tb = TimeBar(100, 102)
     bad_frame_timebar = tb.get_timebar(104)
     assert len(bad_frame_timebar) == 102
     assert bad_frame_timebar == "<                                                                                                   o>"
    def test_timebar_with_marker_short(self):
        tb = TimeBar(100, 8)

        assert len(tb.get_timebar(0)) == 8
        assert tb.get_timebar(0).index("o") == 1
        assert tb.get_timebar(0) == "<o     >"

        assert len(tb.get_timebar(10)) == 8
        assert tb.get_timebar(10).index("o") == 2
        assert tb.get_timebar(10) == "< o    >"

        assert len(tb.get_timebar(50)) == 8
        assert tb.get_timebar(50).index("o") == 4
        assert tb.get_timebar(50) == "<   o  >"

        assert len(tb.get_timebar(99)) == 8
        assert tb.get_timebar(99).index("o") == 6
        assert tb.get_timebar(99) == "<     o>"

        assert len(tb.get_timebar(100)) == 8
        assert tb.get_timebar(100).index("o") == 6
        assert tb.get_timebar(100) == "<     o>"
 def test_frame_num_larger_than_timebar_frame_count(self):
     tb = TimeBar(100, 102)
     bad_frame_timebar = tb.get_timebar(104)
     assert len(bad_frame_timebar) == 102
     assert bad_frame_timebar == "<                                                                                                   o>"
class VT100Player(object):
    """
    Some escape codes used within VT100 Streams
    @see: http://ascii-table.com/ansi-escape-sequences-vt-100.php
    """
    ESC = chr(27)  # VT100 escape character constant
    JMPHOME = ESC + "[H"  # Move cursor to upper left corner
    CLEARSCRN = ESC + "[2J"  # Clear entire screen
    CLEARDOWN = ESC + "[J"  # Clear screen from cursor down

    def __init__(self, movie):
        """
        Player class plays a movie.
        It also stores the current position.
        It exposes the all frame numbers in real values. Therefore not encoded.

        Args:
            movie (ascii_movie.Movie): Movie Object that the player will play.

        """
        self._movie = movie
        self._cursor = 0  # virtual cursor pointing to the current frame
        self._frame_count = 0

        self._stopped = False

        self._clear_screen_setup_done = False

        for f in self._movie.frames:
            self._frame_count += f.display_time

        self.timebar = TimeBar(self._frame_count, self._movie.screen_width)

    def play(self):
        """
        Plays the movie
        """
        self._stopped = False
        for frame in self._movie.frames:
            if self._stopped:
                return
            self._cursor += frame.display_time
            self._load_frame(frame, self._cursor)
            time.sleep(frame.display_time / 15)

    def stop(self):
        """
        Stop the movie
        """
        self._stopped = True

    def _load_frame(self, frame, frame_pos):
        """
        Buffer the the frame and then call draw_frame to display it

        Args:
            frame (ascii_movie.Frame): Frame data to display
            frame_pos (int):  Where the frame falls in the movie

        """
        screenbuf = BytesIO()
        if not self._clear_screen_setup_done:
            screenbuf.write(self.CLEARSCRN.encode())
            self._clear_screen_setup_done = True

        # center vertical, with respect to the time bar (like letter boxing)
        screenbuf.write(self._move_cursor(1, self._movie.top_margin))
        for line in frame.data:
            screenbuf.write((line + "\r\n").encode())

        self._update_timebar(screenbuf, frame_pos)

        # now rewind the internal buffer and fire the public event
        screenbuf.seek(0)
        self.draw_frame(screenbuf)

    def draw_frame(self, screen_buffer):
        """
        Public event method, which can be used to get new Screens.
        This must be implemented by the user.

        Args:
            screen_buffer:  its a file like object containing the VT100 screen buffer

        """
        raise NotImplementedError("You must specify how to draw the frame.")

    def _update_timebar(self, screen_buffer, frame_pos):
        """
        Writes at the bottom of the screen a line like this
        <.......o.....................>
        It should visualize a timeline with 'o' is the current position.

        Args:
            screen_buffer: file like object, where the data is written to
            frame_pos (int): current cursor position on frame

        """
        # Move cursor to the bottom of the screen
        screen_buffer.write(self._move_cursor(1, self._movie.screen_height))

        screen_buffer.write(self.timebar.get_timebar(frame_pos).encode())

    def _move_cursor(self, x, y):
        """
        Send VT100 commands: go to position X,Y

        Args:
            x (int): x coordinate (starting at 1)
            y (int): y coordinate (starting at 1)

        Returns:
            str: the VT100 code as a string

        """
        if 0 >= x > self._movie.screen_width or 0 >= y > self._movie.screen_height:
            sys.stderr.write("Warning, coordinates out of range. ({0}, {1})\n".format(x, y))
            return "".encode()
        else:
            return (self.ESC + "[{0};{1}H".format(y, x)).encode()