
Comment utiliser la commande "script" et un peu de bash + imagemagick pour en faire une animation en GIF (ou pourquoi pas une vidéo par la suite)
Cette semaine, un administrateur système m'a montré une commande que je ne connaissais pas du tout... "script". Ce petit utilitaire permet d'enregistrer le flux terminal vers un fichier afin d'être relu. En grattant dans le "man" j'ai découvert deux options intéressantes, l'une d'elle permet de garder les "temps" de commande + le nombre de caractères à lire... On peut alors utiliser "scriptreplay" pour relire la session ou bien, comme moi, jouer avec et créer une animation
Prenons le temps de comprendre. Si je tape la commande:
script --timing=/tmp/timing /tmp/script
alors commence une session enregistrée dans le fichier /tmp/script.
Tapons alors des commandes, par exemple
echo "hello world"
Puis pressez CTRL+D. La session "script" s'arrête. Vous pouvez rejouer la session (qui ne va pas rejouer les commandes, mais les afficher avec les résultats de l'époque...) en utilisant "cat"
cat /tmp/script
Or... on a besoin de voir "à la replay" la commande. Soit:
scriptreplay -t /tmp/timing /tmp/script
Et là, c'est déjà plus joli. Mais voilà, pour partager une capture de ce genre en vidéo (ou en gif animé) ce format ne nous convient pas encore. On va donc réfléchir recréer une belle vidéo de tout ce beau monde.
Première option, on fait un screencast (avec ffmpeg) mais bon... la souris peut apparaître... et puis pour en faire un gif c'est pas encore ça.
Seconde option, celle qu'on va mettre en place, est de faire ceci: - relire le fichier /tmp/timing - prendre séquence par séquence le texte depuis le typescript (fichier généré par la commande script) - afficher ça dans le terminal et faire une capture du terminal avec la commande "import" - relire une fois de plus le timing et cette fois ci: lire les temps d'attente - utiliser "convert" avec l'option -delay pour mettre frame à frame les images dans le gif animé.
Voilà... bon je vais pas passer par 4 chemins, le but n'est pas de vous apprendre à faire du bash mais au moins vous parler des "soucis" que j'ai dut régler: - lire un fichier d'un certain endroit à un autre: on utilise "dd" - créer des images numérotées avec 5 chiffres pour être à l'aise: printf
Le reste coule presque de source... Voilà le script:
#!/bin/bash TIMING=$1 SCRIPT=$2 W=$WINDOWID rm -rf /tmp/script-replay-gifs/ mkdir /tmp/script-replay-gifs/ t=$(mktemp) cp $SCRIPT $t #remove first line sed -i '1d' $t #clear screen clear #read timing file one by one curr=0 i=0 while read line do #capture time and chars to read cols=($line) chars=${cols[1]} #read from current char the number of chars to read dd if=$t bs=1 skip=$curr count=$chars 2>/dev/null #convert to gif frame with a nice frame-number n=$(printf "%010d" $i) import -winow $WINDOWID /tmp/script-replay-gifs/$n.gif #and move to next position curr=$((curr+chars)) i=$((i+1)) done <$TIMING rm -f $t #now, set gif with delay per frame i=1 while read line do cols=($line) timing=${cols[0]} #get next image file=$(ls -1 /tmp/script-replay-gifs/ | head -n $i | tail -n 1) timing=$(echo "$timing*100" | bc -l | awk '{print int($0)}') command=$command" -delay $timing /tmp/script-replay-gifs/$file" i=$((i+1)) done < $TIMING convert $command /tmp/anim-notoptim.gif convert /tmp/anim-notoptim.gif -coalesce -layers Optimize /tmp/anim.gif rm -f /tmp/anim-notoptim.gif rm -rf /tmp/script-replay-gifs/
Ce script est très très lent à cause de "import" qui prend près d'une seconde par capture... de ce fait pour le moment c'est pas un modèle du genre... et en plus, vous ne pouvez pas quitter le bureau dans lequel vous être pour créer l'animation sous peine de messages d'erreur. Notez que ce n'est un qu'un petit test
Cela donne:
Pour le coup, j'ai quand même posé un petit coup de "gimp" pour optimiser le gif animé. Je vous laisse maître de me faire des remarques et pourquoi pas (si vous le demandez) je créerai un dépôt git quelque part pour qu'on le fasse évoluer.
Update: j'ai ajouté une opération d'optimisation de taille à la fin du script