Exemple #1
0
def render_subcommand(template, cast_filename, svg_filename):
    """Render the animation from an asciicast recording"""
    import termtosvg.asciicast as asciicast
    import termtosvg.term as term

    logger.info('Rendering started')
    asciicast_records = asciicast.read_records(cast_filename)
    replayed_records = term.replay(records=asciicast_records,
                                   from_pyte_char=anim.CharacterCell.from_pyte)
    anim.render_animation(records=replayed_records,
                          filename=svg_filename,
                          template=template)
    logger.info('Rendering ended, SVG animation is {}'.format(svg_filename))
Exemple #2
0
def record_render_subcommand(template, geometry, input_fileno, output_fileno,
                             svg_filename):
    """Record and render the animation on the fly"""
    import termtosvg.term as term

    logger.info('Recording started, enter "exit" command or Control-D to end')
    if geometry is None:
        columns, lines = term.get_terminal_size(output_fileno)
    else:
        columns, lines = geometry
    with term.TerminalMode(input_fileno):
        asciicast_records = term.record(columns, lines, input_fileno,
                                        output_fileno)
        replayed_records = term.replay(
            records=asciicast_records,
            from_pyte_char=anim.CharacterCell.from_pyte)
        anim.render_animation(records=replayed_records,
                              filename=svg_filename,
                              template=template)
    logger.info('Recording ended, SVG animation is {}'.format(svg_filename))
Exemple #3
0
    def test_replay(self):
        theme = AsciiCastV2Theme('#000000', '#FFFFFF',
                                 ':'.join(['#123456'] * 16))

        with self.subTest(case='One shell command per event'):
            nbr_records = 5

            records = [AsciiCastV2Header(version=2, width=80, height=24, theme=theme)] + \
                      [AsciiCastV2Event(time=i,
                                        event_type='o',
                                        event_data='{}\r\n'.format(i).encode('utf-8'),
                                        duration=None)
                       for i in range(1, nbr_records)]

            records = term.replay(records, lambda x: x.data, 50, 1000)
            # Last blank line is the cursor
            lines = [str(i) for i in range(nbr_records)] + [' ']
            for i, record in enumerate(records):
                # Skip header and cursor line
                if i == 0:
                    pass
                else:
                    self.assertEqual(record.line[0], lines[i])

        with self.subTest(case='Shell command spread over multiple lines'):
            records = [AsciiCastV2Header(version=2, width=80, height=24, theme=theme)] + \
                      [AsciiCastV2Event(time=i * 60,
                                        event_type='o',
                                        event_data=data.encode('utf-8'),
                                        duration=None)
Exemple #4
0
        fallback_theme = AsciiCastTheme('#000000', '#000000',
                                        ':'.join(['#000000'] * 16))
        theme = AsciiCastTheme('#000000', '#FFFFFF',
                               ':'.join(['#123456'] * 16))

        with self.subTest(case='One shell command per event'):
            nbr_records = 5

            records = [AsciiCastHeader(version=2, width=80, height=24, theme=theme)] + \
                      [AsciiCastEvent(time=i,
                                      event_type='o',
                                      event_data='{}\r\n'.format(i).encode('utf-8'),
                                      duration=None)
                       for i in range(1, nbr_records)]

            records = term.replay(records, pyte_to_str, None, fallback_theme,
                                  50, 1000)
            # Last blank line is the cursor
            lines = [str(i) for i in range(nbr_records)] + [' ']
            for i, record in enumerate(records):
                # Skip header and cursor line
                if i == 0:
                    pass
                else:
                    self.assertEqual(record.line[0], lines[i])

        with self.subTest(
                case='Shell command spread over multiple lines, no theme'):
            records = [AsciiCastHeader(version=2, width=80, height=24, theme=None)] + \
                      [AsciiCastEvent(time=i * 60,
                                      event_type='o',
                                      event_data=data.encode('utf-8'),
Exemple #5
0
def main(args=None, input_fileno=None, output_fileno=None):
    # type: (List, Union[int, None], Union[int, None]) -> None
    if args is None:
        args = sys.argv
    if input_fileno is None:
        input_fileno = sys.stdin.fileno()
    if output_fileno is None:
        output_fileno = sys.stdout.fileno()

    console_handler = logging.StreamHandler(sys.stderr)
    console_handler.setLevel(logging.INFO)
    console_formatter = logging.Formatter('%(message)s')
    console_handler.setFormatter(console_formatter)
    logger.handlers = [console_handler]
    logger.setLevel(logging.INFO)

    configuration = config.init_read_conf()
    available_themes = config.CaseInsensitiveDict(**configuration)
    del available_themes['global']

    command, args = parse(args[1:], available_themes)

    if args.verbose:
        _, log_filename = tempfile.mkstemp(prefix='termtosvg_', suffix='.log')
        file_handler = logging.FileHandler(filename=log_filename, mode='w')
        file_handler.setLevel(logging.DEBUG)
        file_formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        file_handler.setFormatter(file_formatter)
        logger.handlers.append(file_handler)
        logger.info('Logging to {}'.format(log_filename))

    if command == 'record':
        logger.info(
            'Recording started, enter "exit" command or Control-D to end')
        if args.output_file is None:
            _, cast_filename = tempfile.mkstemp(prefix='termtosvg_',
                                                suffix='.cast')
        else:
            cast_filename = args.output_file

        columns, lines = term.get_terminal_size(output_fileno)
        with term.TerminalMode(input_fileno):
            records = term.record(columns, lines, input_fileno, output_fileno)
            with open(cast_filename, 'w') as cast_file:
                for record in records:
                    print(record.to_json_line(), file=cast_file)

        logger.info('Recording ended, cast file is {}'.format(cast_filename))
    elif command == 'render':
        logger.info('Rendering started')
        if args.output_file is None:
            _, svg_filename = tempfile.mkstemp(prefix='termtosvg_',
                                               suffix='.svg')
        else:
            svg_filename = args.output_file

        if args.font is None:
            font = configuration['GLOBAL']['font']
        else:
            font = args.font

        fallback_theme_name = configuration['GLOBAL']['theme']
        fallback_theme = configuration[fallback_theme_name]
        cli_theme = configuration.get(args.theme)

        records = asciicast.read_records(args.input_file)
        replayed_records = term.replay(
            records=records,
            from_pyte_char=anim.CharacterCell.from_pyte,
            override_theme=cli_theme,
            fallback_theme=fallback_theme)
        anim.render_animation(replayed_records, svg_filename, font)

        logger.info(
            'Rendering ended, SVG animation is {}'.format(svg_filename))
    else:
        # No command passed: record and render on the fly
        logger.info(
            'Recording started, enter "exit" command or Control-D to end')
        if args.output_file is None:
            _, svg_filename = tempfile.mkstemp(prefix='termtosvg_',
                                               suffix='.svg')
        else:
            svg_filename = args.output_file

        columns, lines = term.get_terminal_size(output_fileno)

        if args.font is None:
            font = configuration['GLOBAL']['font']
        else:
            font = args.font

        fallback_theme_name = configuration['GLOBAL']['theme']
        fallback_theme = configuration[fallback_theme_name]
        cli_theme = configuration.get(args.theme)
        with term.TerminalMode(input_fileno):
            records = term.record(columns, lines, input_fileno, output_fileno)
            replayed_records = term.replay(
                records=records,
                from_pyte_char=anim.CharacterCell.from_pyte,
                override_theme=cli_theme,
                fallback_theme=fallback_theme)
            anim.render_animation(replayed_records, svg_filename, font)

        logger.info(
            'Recording ended, SVG animation is {}'.format(svg_filename))

    for handler in logger.handlers:
        handler.close()
Exemple #6
0
def main(args=None, input_fileno=None, output_fileno=None):
    # type: (List, Union[int, None], Union[int, None]) -> None
    if args is None:
        args = sys.argv
    if input_fileno is None:
        input_fileno = sys.stdin.fileno()
    if output_fileno is None:
        output_fileno = sys.stdout.fileno()

    command, args = parse(args[1:])

    logger = logging.getLogger('termtosvg')
    logger.setLevel(logging.INFO)

    console_handler = logging.StreamHandler(sys.stderr)
    console_handler.setLevel(logging.INFO)
    console_formatter = logging.Formatter('%(message)s')
    console_handler.setFormatter(console_formatter)
    logger.handlers = [console_handler]

    if args.verbose:
        file_handler = logging.FileHandler(filename=LOG_FILENAME, mode='w')
        file_handler.setLevel(logging.DEBUG)
        file_formatter = logging.Formatter(
            '%(asctime)s - %(name)s - %(levelname)s - %(message)s')
        file_handler.setFormatter(file_formatter)
        logger.handlers.append(file_handler)
        logger.info('Logging to {}'.format(LOG_FILENAME))

    fallback_theme_name = 'solarized-dark'
    xresources_str = term.default_themes()[fallback_theme_name]
    fallback_theme = asciicast.AsciiCastTheme.from_xresources(xresources_str)

    if command == 'record':
        logger.info(
            'Recording started, enter "exit" command or Control-D to end')
        if args.output_file is None:
            _, cast_filename = tempfile.mkstemp(prefix='termtosvg_',
                                                suffix='.cast')
        else:
            cast_filename = args.output_file

        columns, lines, theme = term.get_configuration(output_fileno)
        with term.TerminalMode(input_fileno):
            records = term.record(columns, lines, theme, input_fileno,
                                  output_fileno)
            with open(cast_filename, 'w') as cast_file:
                for record in records:
                    print(record.to_json_line(), file=cast_file)

        logger.info('Recording ended, cast file is {}'.format(cast_filename))
    elif command == 'render':

        def rec_gen():
            with open(args.input_file, 'r') as cast_file:
                for line in cast_file:
                    yield asciicast.AsciiCastRecord.from_json_line(line)

        logger.info('Rendering started')
        if args.output_file is None:
            _, svg_filename = tempfile.mkstemp(prefix='termtosvg_',
                                               suffix='.svg')
        else:
            svg_filename = args.output_file

        if args.theme is None:
            theme = fallback_theme
        else:
            xresources_str = term.default_themes()[args.theme]
            theme = asciicast.AsciiCastTheme.from_xresources(xresources_str)

        replayed_records = term.replay(rec_gen(), anim.CharacterCell.from_pyte,
                                       theme)
        anim.render_animation(replayed_records, svg_filename)

        logger.info(
            'Rendering ended, SVG animation is {}'.format(svg_filename))
    else:
        # No command passed: record and render on the fly
        logger.info(
            'Recording started, enter "exit" command or Control-D to end')
        if args.output_file is None:
            _, svg_filename = tempfile.mkstemp(prefix='termtosvg_',
                                               suffix='.svg')
        else:
            svg_filename = args.output_file

        columns, lines, system_theme = term.get_configuration(output_fileno)

        if args.theme is None:
            if system_theme is None:
                theme = fallback_theme
            else:
                theme = system_theme
        else:
            xresources_str = term.default_themes()[args.theme]
            theme = asciicast.AsciiCastTheme.from_xresources(xresources_str)

        with term.TerminalMode(input_fileno):
            records = term.record(columns, lines, theme, input_fileno,
                                  output_fileno)
            replayed_records = term.replay(records,
                                           anim.CharacterCell.from_pyte, theme)
            anim.render_animation(replayed_records, svg_filename)

        logger.info(
            'Recording ended, SVG animation is {}'.format(svg_filename))

    for handler in logger.handlers:
        handler.close()