Скринкаст терминала с помощью asciinema

zhf8-wixqzzimq5hyylkshpbqks.jpeg

Вы наверняка знакомы с asciinema (github), это удобный опенсорсный инструмент для записи действий в терминале. Записи сохраняются в простом для чтения текстовом формате, поэтому весят совсем немного, а веб-плеер по сути воспроизводит текст из терминала вместо видео, так что любой кусок можно скопировать и использовать. Готовый материал можно загрузить в одно нажатие на asciinema.org или сначала отредактировать локально. Плеер можно встроить на сайт буквально в три строки, бонусом прилагаются всякие плюшки с оформлением и совместимостью, и вообще по совокупности всех фич (и отсутствия головной боли) asciinema давно перерос все аналоги. Вот только есть несостыковка: записи в проекте называют asciicasts, по аналогии со скринкастами —, но возможности стримить сессию в реальном времени не было несколько лет, пока не вышел релиз 2.0, в котором с помощью нового формата файлов удалось реализовать на удивление стабильную и удобную раздачу на любой терминал в реал-тайме. О том, как это работает, о подводных камнях и перспективах — под катом.

Один формат — много возможностей


В старых версиях данные записывались в JSON, что добавляло немало оверхеда, да и парсинг у него нетривиальный, а главное, объект неудобно дробить на чанки для частичной передачи и чтения данных. На смену пришёл кастомный формат:
  {"version": 2, "width": 236, "height": 54, "timestamp": 1613998795, "idle_time_limit": 1.0, "env": {"SHELL": "/bin/bash", "TERM": "screen"}}
  [0.023635, "o", "client@some-desktop:~$ "]
  [0.812065, "o", "h"]
  [1.087183, "o", "e"]
  [1.246706, "o", "l"]
  [1.473065, "o", "l"]
  [1.657363, "o", "o"]
  [4.114169, "o", "exit\r\n"]

В первую строку записываются метаданные и параметры воспроизведения, в каждую последующую — очередная строка терминала. На вид очень похоже на JSON, но теперь можно отображать любую строку по отдельности, добавив к ней метаданные. Теперь можно не только передавать данные по частям, но и дописывать новый каст в конец старого файла.

Если раньше единственным способом синхронного вывода было перенаправление записи в stdin второго терминала, что подразумевало хранение всего каста в памяти и воспроизведение после полного получения и парсинга, то теперь воспроизведение начинается сразу после анализа первой строки. Поток можно, например, завернуть в юниксовый именованный канал:

  mkfifo /tmp/demo.pipe

  # viewing terminal
  asciinema play /tmp/demo.pipe
  
  # recording terminal
  asciinema rec /tmp/demo.pipe

Или можно передавать данные по сети с помощью netcat:
  # viewing terminal (hostname: node123)
  asciinema play <(nc -l localhost 9999)
  
  # recording terminal
  asciinema rec >(nc node123 9999)

Причём на принимающей стороне необязательно даже иметь установленный asciinema, с режимом --raw данные отправляются «как есть», без конвертации. Но там не будет метаданных, не будут сохранены тайминги и задержки, поэтому такой вариант не слишком привлекателен. Гораздо лучше шагнуть ещё дальше и стримить сессию по ssh! Но здесь лучше не торопиться и сначала разобраться в реализации.

Рисуем картинку локально


Установка asciinema:
  sudo apt install python3-pip
  sudo pip3 install asciinema

Запишем тестовый файл командой asciinema rec -i 1 file.cast. -i 1 — это параметр, обрезающий задержку при вводе до одной секунды.

Пример файла каста выше соответствует выводу cat file.cast, он статичен. Но если начать запись и в соседней вкладке запустить tail -f file.cast, строки будут появляться по мере дописывания в файл после изменений в терминале. Этот динамически обновляющийся вывод можно перенаправить в asciinema play:

  # принимающая вкладка
  tail -fn +1 file.cast | asciinema play -

Таким нехитрым образом мы запускаем зеркальное отображение наших действий в записываемой вкладке. Но картинка будет соответствовать источнику только если плеер изначально принял первую строку с параметрами (в частности, -i) и успел «догнать» стрим. Чтобы избежать проблем с задержкой просто будем передавать вместе с последней строкой первую:
  # принимающая вкладка
  (head -1 file.cast && tail -fn 0 file.cast) | asciinema play -

К сожалению, при этом ломается первая строка в «зеркале» — она начинается сразу с введённых символов, опуская префикс, но это не критично, да и достаточно одного переноса, чтобы всё вернулось:

bfq01ylf0kswlzyfc3ejwilmn5a.png

В целом, для простых юзкейсов можно не запариваться head’ом, но если строить полноценную систему с подключением посередине стрима и восстановлением после обрыва соединения, конечно, передавать первую строку нужно обязательно.

Переносим отображение на удалённый сервер


Нам понадобится беспарольный доступ, поэтому в принимающей вкладке генерируем ключи RSA, добавляем публичный ключ на сервер и идём его настраивать:
  ssh-keygen -t rsa
  ssh-copy-id -i ~/.ssh/id_rsa.pub user@hostname
  ssh user@hostname

  # на сервере поставим также утилиту pv для буферизации
  sudo apt install python3-pip pv
  sudo pip3 install asciinema

После установки осталось перенаправить поток текста в файл на сервере:
  # клиент
  asciinema rec -i 1 >(ssh user@hostname tee /path/to/file.cast >/dev/null)
  # сервер
  tail -fn +1 /path/to/file.cast | asciinema play -

Готово! По сути, всё уже работает. Но стоит добавить ещё пару штрихов: во-первых, asciinema play — высокоуровневая команда, для которой можно определить скорость выполнения операций. Чем выше значение (опционального) параметра -s, тем меньше задержка и выше нагрузка. Для наших целей куда лучше подойдёт asciinema cat, который работает на запись как аналог режима --raw, но воспроизводится с сохранением метаданных и параметров. Кроме того, воспользуемся pv для создания буфера на случай скачков производительности или сетевых лагов:
  # сервер
  tail -fn +1 /home/benaryorg/.local/tmp/tmp.6nkIacxYqF/foo | pv -q -b 8m | asciinema cat -

Результат

393164.svg
Плеер не встраивается в Хабр, извините

Светлое будущее


Вообще вся эта чудесная функциональность появилась довольно давно: 2.0 вышел около трёх лет назад. Однако, хорошая новость в том, что весь предыдущий год команда asciinema-player переписывала плеер целиком чтобы добавить в него вебсокеты, event sourcing и полностью сделать его совместимым с настоящими скринкастами. И хотя в профильном issue нет активности с весны, по коммитам видно, что работа кипит. Будет очень интересно посмотреть на стриминговый сервис для воинов консоли, не находите?

На правах рекламы


Подыскиваете VDS для отладки проектов, сервер для разработки и размещения? Вы точно наш клиент :) Посуточная тарификация, создавайте собственный конфиг в несколько кликов.

8p3vz47nluspfyc0axlkx88gdua.png

© Habrahabr.ru