Enhance your asciinema recordings
Asciinema is great. It lets you create textmode screen recordings like this one:
The downside is that I'm a slow typist, and you have to sit there and watch me type out the commands in real time. It's annoying.
No problem,
we can use the builtin
--idle-time-limit
option to limit the time between events
to 0.1 seconds:
But somethings is still missing.
There is no time for the viewer to read the command before it is run.
There is also no time for the viewer to read the
output of one command before the next one starts appearing
on screen.
If the original recording felt too slow,
this one is way too fast!
In addition to that,
the loading time of important-command.sh
is also limited, making it appear faster than it
actually is.
Let's go a step further. We can write a Python script that will change the timings of events based on the following rules:
- Set the timing for all keystrokes to exactly 0.05 seconds
- Set the timing before running a command to 2 seconds, giving the viewer a chance to read the command arguments
- Set the timing after running a command to 2 seconds, giving the viewer a chance to read the output.
- For all other events, keep the timing the same.
Here is what is looks like:
Doesn't that look so much nicer?
If you want to apply the same effect to your asciinema recordings, you can use my script below. The script takes two arguments: the input filepath and the output filepath.
Note that you will probably need to tweak it,
in particular the PROMPT
constant at the top of the file.
This should correspond to the string captured by
asciinema when a command finishes running
and the shell displays a prompt.
I've set it to the value that works for a simple $
prompt.
You will need to change it if your prompt is different.
If everything works correctly, the script should print
a message to stderr
whenever it adds extra time
before and after a command is run in the asciinema recording.
#!/usr/bin/env python3
from sys import stderr
import re
from pathlib import Path
import argparse
PROMPT = r'$ \u001b[6n'
def cli():
parser = argparse.ArgumentParser()
parser.add_argument("input", type=Path, help="Input file path")
parser.add_argument("output", type=Path, help="Output file path")
args = parser.parse_args()
args.output.write_text(
'\n'.join(enhance(args.input))
)
def enhance(input_path):
lines = []
for line in input_path.read_text().split('\n'):
match = re.match(r'\[(\d+\.\d+), "o", "(.*)"\]', line)
if match:
lines.append((
float(match.groups()[0]),
match.groups()[1],
line
))
else:
lines.append((None, None, line))
time = 0.0
last_line_time = 0.0
last_line_length = 0
for i, (line_time, string, line) in enumerate(lines):
if not line_time:
yield line
continue
if string == r'\r\n':
# Time before next command
# (to let the viewer read the output)
print('Added time before next command', file=stderr)
time += 2
elif len(string) == 1:
# Timing between keystrokes
time += 0.05
if (
i - 1 >= 0
and
lines[i - 1][1] == PROMPT
):
# Time before executing command
# (to let the viewer read your command)
print('Added time before running command', file=stderr)
time += 2
else:
time += line_time - last_line_time
yield f'[{time:.6f}, "o", "{string}"]'
last_string = string
last_line_time = line_time
last_line_length = len(string)
if __name__ == '__main__':
cli()