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



    Вы наверняка знакомы с 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 -
    

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



    В целом, для простых юзкейсов можно не запариваться 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 -
    

    Результат




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

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


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



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


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

    VDSina.ru
    Серверы в Москве и Амстердаме

    Комментарии 4

      +1

      script / scriptreplay существуют в юниксах с 1980-х. :)


      Крутой инструмент, к сожалению малоизвестный, и файлы записи в общем случае не переносимы между терминалами. Основной плюс — идеальное изображение и шрифт при проигрывании, не достижимые никаким FullHD скринкастом, тк scriptreplay / asciinema это не запись экрана, а вывод обычного текста на терминал, с родным шрифтом, сглаживанием ит.д.


      Есть важный дополнительный инструмент к этим рекордерам, не упомянутый в статье, — кодировщик в gif или видеофайл (congif, etc).

        0
        Недавно статья про сиксели пробегала.
          +4
          Boomburum Вот бы хабр добавил этот прекрасный проигрыватель ;)
            0

            Источник картинки (потока) — один. Это понятно. Источник же.
            Получатель в примерах статьи указан тоже один.
            Но в "традиционных" стримингах получателей-то бывает много.
            Как это достичь в сабже? Делением потока из источника программой tee на множество файлов? Ну всё-равно это будет даже если и очень большое количество получателей потока, но всё-равно количество получателей будет ограниченным и заранее известным.
            А как наваять стриминг такой, чтобы количество получателей потока могло быть любым?

            Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.

            Самое читаемое