четверг, 29 октября 2015 г.

Скринкасты и UNIX-way

Не так давно обсуждали с коллегами разницу между возможностями command-line в Windows и Linux. Мне кажется так и не удалось донести мысль, что тот же powershell не смотря наличие нескольких хороших идей в большинстве своём остаётся для повседневных задач неудобным, неповоротливым монстром. И что пресловутый UNIX-way, с сотнями утилит на все случаи жизни гораздо практичнее и функциональнее. Сотни тысяч приложений, написанных в стиле максимальной конфигурируемости, позволяют как из кубиков собирать нужное и максимально удобное для себя родимого. Для иллюстрации подхода, попробую на примере записи скринкастов в GIF, показать насколько удобно можно собирать эти кубики.


Итак, хочется простого: возможности записи окна или выделенного региона в GIF, причём прерывать запись нужно по горячей клавише, что бы в конце не записывать как курсор идёт в трей и выключает запись. Ну и хочется, что бы результат складывался в нужную директорию с уникальным именем, а путь к нему копировался в буфер обмена. Ну и что бы работало мгновенно и без ненужных окошек. Вроде все желания понятные, а готового приложения ни под Windows, ни под Linux я не нашёл.

Ну что же, попробуем реализовать все хотелки сами. Для записи будем использовать шустрый byzanz, вряд ли он будет в дефолтной поставке, поэтому установим его:
yaourt -S byzanz
Если посмотреть документацию byzanz-record, то видно, что сам по себе он не умеет ни создавать уникальный файл для сохранения, ни выбирать область для записи, зато всё можно передать через command-line, вплоть до такой мелочи - как записывать курсор мыши или нет (о чем готовые комбайны для скринкастов почему-то часто забывают). Например запись 5 сек в GIF без курсора делается вот так:
byzanz-record --cursor=false --duration=5 ~/tmp/image.gif
Идём дальше, что-бы каждый новый файл не перезаписывал предыдущий, добавим timestamp в имя файла, что бы получить путь вроде такого "~/tmp/image-2015-12-12T12-12-12.gif":
byzanz-record --cursor=false --duration=5 ~/tmp/image-$(date +%FT%H-%M-%S).gif
Что бы можно было выбирать окно, которое будем записывать, воспользуемся утилитой xwininfo из поставки xorg и создадим скрипт:
#!/bin/bash
xwininfo=$(xwininfo -shape)

getnum()
{
    echo "$xwininfo" | grep "$1" | tr -cd [:digit:]
}

x=$(getnum "Absolute upper-left X:")
y=$(getnum "Absolute upper-left Y:")
w=$(getnum "Width:")
h=$(getnum "Height:")
path=~/tmp/image-$(date +%FT%H-%M-%S).gif

byzanz-record --cursor=false --duration=5 --x=$x --y=$y --width=$w --height=$h $path
После вызова этого скрипта, курсор мыши изменится на "прицел", предлагая выбрать какое окно нужно записывать, после выбора мы получим его координаты и передадим в byzanz.

Теперь разберёмся с произвольным времени записи. Просто задать большой duration и потом принудительно завершать byzanz нельзя, т.к. он не запишет GIF до конца и последний не везде будет корректно воспроизводиться. Но если внимательно посмотреть на документацию, то там можно найти опцию:
-e, --exec=COMMAND
    Instead of specifying the duration of the animation, execute the given COMMAND and record until the command
 exits. This is useful both for benchmarking and to use more complex ways to stop the recording, like writing
 scripts that listen on dbus.
Отлично, заставим byzanz запустить другое приложение и когда мы его прибьём, запись завершится, а GIF файл успешно допишется до конца. При этом нужно гарантировать, что если завершить запись мы по какой-то причине забыли, что бы не забивать диск, она должна сама закончится допустим через 5 минут (для моих нужд этого хватит с лихвой). Тут на помощь прийдет утилита "sleep", теперь строчка запуска будет выглядеть в скрипте вот так:
...
byzanz-record --cursor=false --exec="sleep 5m" --x=$x --y=$y --width=$w --height=$h $path
Теперь завершить запись можно простой командой "pkill sleep", т.к. я пользуюсь awesome, то мне не составит труда добавить горячую клавишу выполняющую эту команду, прописав в конфиг:
awful.key({ modkey, "Shift"   }, "e", function () awful.util.spawn("pkill sleep") end),
Ну и в качестве "изюминки" добавим нотификацию на экране о завершении записи (вдруг промахнусь с горячей клавишей завершения), а так же не забудем копирование пути к файлу в буфер обмена. Добавим в конец скрипта:
...
printf $path | xsel -ib
notify-send -u 'normal' 'Screencast-window' 'Finish'
Итого конечный вариант выглядит вот так.

Для того, что бы записывать не всё окно, а какой-то регион, понадобится ещё одна небольшая утилита xrectsel:
yaourt -S xrectsel
Ну а дальше, всё как и раньше, конечный скрипт можно найти тут.

Для запуска приложений я пользуюсь rofi поэтому мне вышеописанный скрипты некуда прописывать не нужно, достаточно подложить в один из PATH путей.

Итого: я написал 2 скрипта общей длинной в 20 строк, добавил 1 строчку в конфиг и установил 2 небольшие утилиты. По времени - я писал скрипт сильно меньше, чем потратил на попытки найти уже готовое решение с такими свойствами. Причём получившиеся скрипты у меня теперь сохранены на GitHub, необходимые пакеты там же и в случае чего я восстановлю всё это в пару команд.

Что характерно на powershell вышеописанное частично можно реализовать, но с гораздо большими усилиями, а частично не реализуется вообще, без вкорячивания туда пары страниц кода на C#, ну а про возможность установить необходимые приложения из командной строки и говорить не буду.

2 комментария:

  1. О, мысль про выключение записи по клавише мне нравится, как-то не пришло в голову.

    А то я ж простой убунтоид, взял аналогичный скриптик отсюда http://askubuntu.com/a/201018 и немного мучаюсь с тем, что время записи надо указывать наперед - не всегда удобно.

    ОтветитьУдалить
    Ответы
    1. Я собственно по мотивам тех скриптов и делал, добавив туда немного удобства для себя.

      Удалить